<script setup>
import { nextTick, onMounted, reactive, ref, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useStore } from '../stores/index'
import {
  stGsW, amplify, downArrow, pi, ports, data, scopeOption, scopeMenu, searchFn, getBranchEdge,
  changeModelData, pollFn, objV2S, objV2N, isNum, compareW, source_json, subSystemDefaultCells,
  systemSetPorts, isArr, getLatex, isExp, isObj, isEquObj, getMObjVal, setMObjVal
} from './allConfig.js'
import katex from 'katex'
import axios from 'axios'
import { post, get } from '../request/axiosset'
import * as d3 from 'd3'
import * as echarts from 'echarts'
import { ArrowRight } from '@element-plus/icons-vue'
import $ from 'jquery'

import { Graph } from '@antv/x6'
import { Selection } from '@antv/x6-plugin-selection'
import { Keyboard } from '@antv/x6-plugin-keyboard'
import { Clipboard } from '@antv/x6-plugin-clipboard'
import { Stencil } from '@antv/x6-plugin-stencil'
import { Snapline } from '@antv/x6-plugin-snapline'
import { Transform } from '@antv/x6-plugin-transform'
import { MiniMap } from '@antv/x6-plugin-minimap'
import { Export } from '@antv/x6-plugin-export'
import { History } from '@antv/x6-plugin-history'

import icons from '../components/icons/icons'
import Menu from '../views/Menu.vue'
import TopMenu from '../views/TopMenu.vue'
import Tabs from '../views/Tabs.vue'
import autoCom from '../components/autoCom.vue'
import SplitBox from '../views/SplitBox.vue'
import Work from './Work.vue'
// import * as Y from 'yjs'
// import { WebsocketProvider } from 'y-websocket'

// const doc = new Y.Doc()
// const wsProvider = new WebsocketProvider('ws://localhost:1234', 'my-roomname', doc)

// wsProvider.on('status', event => {
//   console.log(event, event.status) // 打印 "已连接" 或 "已断开"
// })

// const yarray = doc.getArray('my array')
// yarray.insert(0, ['hello', 'world'])
// yarray.observe(event => {
//   // Log a delta every time the type changes
//   // Learn more about the delta format here: https://quilljs.com/docs/delta/
//   console.log('delta:', event.changes.delta)
// })








const router = useRouter()
const route = useRoute()
const store = useStore()



const visScopes = reactive([])

onMounted(async () => {
  // console.log('🤴', router, route)
  if (route.name == 'children' && !breadcrumb.arr.length) {
    router.replace('/')
  }
  if (route.query.id) {
    store.SavedInformationId = route.query.id
    // store.SavedInformationData = null
    await reOpen()
  }
})




const isAirEdge = edge => !edge.edgeGroup && (edge.source.x || edge.target.x);









function Json2Antv(json, parent) {
  console.log('🤵', json, parent)
  if (json.System.config) {
    glProps.data.Time = json.System.config.Time
    glProps.data.stepTime = json.System.config.stepTime
    // glProps.data.speed = json.System.config.speed
  }
  const expandEdges = json.System.connections?.map(i => {
    if (i.edgeGroup) {
      const branch2Arr = (e, arr = [{
        ...i,
        inPoint: i._point,
        isOutEdge: true
      }]) => {
        for (const f of e._branch) {
          let obj = {
            edgeGroup: i.edgeGroup,
            outPoint: e._point,
            ...f
          }
          if (f._branch) {
            branch2Arr(f, arr)
            if (f._point) {
              obj.isBranch = true
              obj.inPoint = f._point
            }
          } else {
            obj = { ...obj, ...f, isInEdge: true }
          }
          arr.push(obj)
        }
        return arr
      }
      const b2arr = branch2Arr(i)
      console.log('😢', i, b2arr)
      return b2arr
    }
    return i
  }).flat()
  const edges = expandEdges.map(i => {
    console.log('🙍‍♂️', i)
    let obj = {}

    // console.log('👤', obj, json.System.nodes.find(j => j['-ID'] == i.inNodeId),
    //   json.System.nodes.find(j => j['-ID'] == i.outNodeId))
    obj = {
      source: {
        outNodeId: i.outNodeId,
        outPortIndex: i.outPortIndex,
        cell: json.System.nodes.find(j => j['-ID'] == i.outNodeId)?.uuid,
        port: '_out_' + i.outPortIndex
        // port: i.outNodeId + '_out_' + i.outPortIndex
      },
      target: {
        inNodeId: i.inNodeId,
        inPortIndex: i.inPortIndex,
        cell: json.System.nodes.find(j => j['-ID'] == i.inNodeId)?.uuid,
        port: '_in_' + i.inPortIndex
        // port: i.inNodeId + '_in_' + i.inPortIndex
      }
    }



    if (i.edgeGroup) {
      if (i.isOutEdge || i.isBranch) {
        obj.target = objV2N(i.inPoint)
        obj.attrs = {
          line: {
            stroke: '#000',
            strokeDasharray: 0,
            targetMarker: {
              name: 'circle',
              r: 3,
              cx: -1
            }
          }
        }
      }
      if (i.isInEdge || i.isBranch) {
        obj.source = objV2N(i.outPoint)
      }
    }
    if (i.isAirEdge) {
      if (i.inNodePoint) {
        obj.target = objV2N(i.inNodePoint)
      }
      if (i.outNodePoint) {
        obj.source = objV2N(i.outNodePoint)
      }
    }



    return {
      shape: 'edge',
      attrs: {
        line: {
          stroke: '#000',
          strokeDasharray: 0,
          targetMarker: {
            name: 'classic',
            offset: 4,
            size: 16
          }
        }
      },
      ...obj
    }
  })
  console.log('🚶‍♂️', edges)
  const nodes = json.System.nodes?.map((i, index) => {
    const NT = i['-NodeType'].split('/')
    // console.log('🤷‍♀️', NT, parent)
    if (NT[1] == 'Subsystem') {
      json.System.nodes[index].childrenCells =
        Json2Antv({ System: i['internal-data']['childrenSystem'] }, i.uuid)
    }
    const j = data.nodes.find(i => i.label == NT[1])
    const outPorts = j.ports.items.filter(m => m.type == 'chu')
    const inPorts = j.ports.items.filter(m => m.type != 'chu')
    // let outPorts = j.ports.items.filter(m => m.type == 'chu')
    // let inPorts = j.ports.items.filter(m => !m.type)
    // if (outPorts.length && outPorts.every(port => port.group == 'ellipse')) {
    //   outPorts.reverse()
    //   console.log('😬', outPorts)
    // }
    // if (inPorts.length && inPorts.every(port => port.group == 'ellipse')) {
    //   inPorts.reverse()
    //   console.log('🗣', i, j, inPorts)
    // }
    // const egdesPorts = edges.reduce((m, n) => [...m, n.source.port, n.target.port], [])
    const getPortJson = (ports, name) => ports.map((m, n) => ({
      ...m,
      attrs: {
        ...m.attrs, path: {
          ...m.attrs.path,
          // visibility: egdesPorts.includes(name + '_' + n) ? 'hidden' : 'visible'
        }
      },
      id: name + '_' + n
    }))

    return {
      ...j,
      id: i.uuid,
      // id: i['-ID'],
      uid: i['-ID'],
      group: NT[0],
      ts: NT[1],
      ...objV2N(i.position),
      ...objV2N(i.size),
      ports: {
        ...j.ports,
        items: [
          ...getPortJson(outPorts, '_out'),
          ...getPortJson(inPorts, '_in')
          // ...getPortJson(outPorts, i['-ID'] + '_out'),
          // ...getPortJson(inPorts, i['-ID'] + '_in')
        ]
      }
    }
  }) || []

  console.log('转化前', [...nodes, ...edges], parent);
  let arr = graph.parseJSON([...nodes, ...edges])
  console.log('转化后', arr, parent);
  arr.map((i, j, _arr) => {
    if (j < nodes.length) {     //组件存值
      // console.log(json.System.nodes[j]);
      ['-Ports', '-SID', '-ID', '-NodeType', 'internal-data', 'childrenCells', 'uuid', '_customTemplate']
        .forEach(n => i[n] = json.System.nodes[j][n])
    } else {     //连线存值
      ['edgeGroup', 'isOutEdge', 'isBranch', 'isInEdge', 'isAirEdge']
        .forEach(n => {
          // console.log('🧶', n, j, j - nodes.length, json.System.connections, expandEdges)
          i[n] = expandEdges[j - nodes.length][n]
        })
      const vertices = expandEdges[j - nodes.length]?.vertices
      console.log('👕', j, nodes.length, j - nodes.length, expandEdges[j - nodes.length], vertices)
      if (vertices && vertices.length) i.vertices = objV2N(vertices)
      // console.log('🧖‍♀️', i, vertices, i.vertices, expandEdges[j - nodes.length])
    }
    if (i.isEdge()) {
      // const { ports: { items: sArr } } = _arr.find(n => n.id == i.source.cell)
      // const { ports: { items: tArr } } = _arr.find(n => n.id == i.target.cell)
      // i.setSource({ ...i.source, port: sArr.filter(n => n.type == 'chu')[i.source.outPortIndex].id })
      // i.setTarget({ ...i.target, port: tArr.filter(n => n.type != 'chu')[i.target.inPortIndex].id })

      // console.log(i, _arr, sArr, tArr);
    } else if (i.isNode()) {
      const NT = i['-NodeType'].split('/')
      i.id = i.uuid
      i.uid = i['-ID']
      i.sid = i['-SID']
      i.group = NT[0]
      i.ts = NT[1]
    }
  })
  // console.log(arr);
  return arr
}

const cModelData = (...args) => changeModelData([
  graph, dialogObj, route, router, breadcrumb, nodeTem, katex
], ...args);
function nodeNUll(w) {
  return {
    width: data.nodes.map(e => e.width).reduce((i, j) => Math.max(i, j), w || 200),
    attrs: { nodeNUll: true }
  }
}

const languang = reactive({})
const activeName = ref(0)

let setEvent = reactive({
  con: 0,
  help: 0,
  yingyong: 0,
  disList: []
})
function confirm(e) {
  setEvent.actDialog = e
  setEvent.con++
}
function help() {
  setEvent.help++
}
function yingyong(e) {
  setEvent.actDialog = e
  setEvent.yingyong++
}
let glProps = reactive({
  topH: 120,
  gap: 4,
  stencil: {
    w: nodeNUll().width + 15,
    is: true
  },
  right: {
    w: 340,
    is: true
  },
  terminal: {
    w: 340,
    is: true
  },
  data: {
    Time: 10,
    stepTime: 0.01,
    // speed: ''
  },
  progress: {
    p: 0,
  }
})
let nodeCount = {
  In: 1,
  Out: 1,
  Integrator: 1,
  From: 0,
  Goto: 0,
  Step: 1,
  Lookup_1D: 1,
  RalationalOperator: 1,
  RateLimiter: 1
}
const nodeTem = reactive({})
const dialogObj = reactive({})
const breadcrumb = reactive({})
//   arr: [{
//     name: route.params.children,
//     path: route.fullPath
//   }],
//   carr: [{
//     name: route.params.children,
//     level: 1,
//     children: route.params.children,
//     path: route.fullPath
//   }]
// })
const breadcrumbInit = () => {
  // const { params, query } = route
  // const { json, name } = store.SavedInformationData
  // if (!params.children) return;
  // let paramsArr = params.children.split('>').map(i => ({ name: i }))
  // console.log('🦸', paramsArr)
  // if (Object.keys(json).length && name == paramsArr[0].name) {
  //   const cells = Json2Antv(json)
  //   paramsArr = cells2list(cells, name)
  //   console.log('🦸', cells, paramsArr)
  //   function cells2list(cells, name, level = 1, arr = []) {
  //     arr.push({
  //       name: name,
  //       level: level,
  //       cells: cells
  //     })
  //     for (const i in cells) {
  //       if (Object.hasOwnProperty.call(cells, i)) {
  //         const e = cells[i];
  //         if (e.childrenCells) {
  //           cells2list(e.childrenCells, e.label, ++level, arr)
  //         }
  //       }
  //     }
  //     return arr
  //   }
  // }
  // const _arr = paramsArr.map((i, j, k) => ({
  //   name: i.name,
  //   level: i.level,
  //   children: k.slice(0, j + 1).map(n => n.name).join('>'),
  //   path: `/edit/${k.slice(0, j + 1).map(n => n.name).join('>')}?${Object.keys(query)
  //     .map(n => `${n}=${query[n]}`).join('&')}`,
  //   cells: i.cells
  // }))
  // console.log('👩‍👩‍👦', _arr)


  const _arr = [{
    name: route.params.children,
    level: 1,
    children: route.params.children,
    path: route.fullPath
  }]
  breadcrumb.arr = _arr
  breadcrumb.carr = _arr
}
const Scope = reactive({
  is: true,
  clickMenu(e, con) {
    let arr = e.index.split('-')
    let res
    arr.forEach((i, j) => {
      if (!j) {
        res = Scope.menu[i]
        return
      }
      if (res.item) {
        res = res.item[i]
      } else {
        res = res[i]
      }
      // console.log(i, j, res);
    })
    res.value = !res.value
    console.log(e, res)

    if (res.fn) {
      // let scope = echarts.getInstanceByDom(document.getElementById('Scope'))
      // const params = [res, scope, e, con]
      const params = [res, e, con]
      res.fn(...params)
    }
  },
  status: ['就绪', '基于采样', 10],
  menu: scopeMenu
})

function cancelDom(arr) {
  for (const e of arr) {
    let dom = document.getElementById(e.id)
    if (dom && e.ref) e.ref.is = false
    if (e.fn) e.fn()
  }
}
let selectArea = reactive({})
let WorkData = reactive({
  tableData: [],
  value: ''
})




let graph;
let graphContainer;
onMounted(() => {
  graphContainer = document.getElementById('container')
  graph = new Graph({
    container: graphContainer,
    autoResize: true,
    connecting: {
      snap: {
        radius: 15
      },
      allowLoop: false,
      allowNode: false,
      allowMulti: false, //允许多条边
      allowEdge: true,
      // allowPort: false,
      // highlight: true,
      validateEdge({ edge, edge: { target }, previous }) {
        if (
          target.x &&
          Math.abs(target.x - previous.x) < 20 &&
          Math.abs(target.y - previous.y) < 20
        ) {
          ElMessage.warning('绘制线段过短')
          edge.remove()
          return false
        }
        return true
      },
      // connector: {
      //   name: 'jumpover',
      //   args: {
      //     type: 'cubic',
      //     size: 7
      //   }
      // },
      router: {
        name: 'orth',
        args: {
          padding: {
            vertical: 500,
            // top: 350,
            // bottom: 350,
            // left:1000,
            // right:1000,
          }
        }
        // name: 'manhattan',
        // args: {
        //   padding: {
        //     left: 0,
        //     top: 150,
        //     bottom: 15,
        //     right: 0
        //   },
        //   startDirections: ['right'],
        //   endDirections: ['left']
        // }
      },
      connectionPoint: 'anchor',
      anchor: 'left',
      edgeAnchor: 'closest'
    },
    grid: {
      visible: true,
      type: 'doubleMesh',
      size: 10,
      args: [
        { thickness: 0 },
        {
          color: '#ddd',
          thickness: 1,
          factor: 2
        }
      ]
    },
    // scaling: {
    //   min: 0.5,
    //   max: 10
    // },
    highlighting: {
      magnetAvailable: {
        name: 'stroke',
        args: {
          padding: 4,
          attrs: {
            'stroke-width': 2,
            stroke: '#FEB663'
          }
        }
      }
    },
    translating: {
      restrict: true
    },
    mousewheel: {
      enabled: true,
      modifiers: ['alt']
    },
    panning: {
      enabled: true,
      modifiers: ['alt']
    },
    interacting: {
      edgeMovable: false
    },
    // width: '100%',
    // height: '100%',
    background: {
      color: '#F2F7FA'
    }
  })
  graph.translate(-1, -1)
  // graph.fromJSON(data) // 渲染元素
  // graph.centerContent() // 居中显示
  graph.use(
    new Selection({
      enabled: true,
      multiple: true,
      rubberband: true,
      movable: true,
      // showNodeSelectionBox: true,
      // showEdgeSelectionBox: true,
      // pointerEvents: 'none'
    })
  )
  graph.use(
    new Keyboard({
      enabled: true,
      global: true
    })
  )
  graph.use(
    new Clipboard({
      enabled: true
    })
  )
  graph.use(
    new Snapline({
      enabled: true,
      tolerance: 1
    })
  )
  graph.use(
    new Transform({
      resizing: {
        enabled: true,
        // preserveAspectRatio: true,
        orthogonal: false,
        allowReverse: false
        // minWidth: 60,
        // minHeight: 60
      },
      rotating: {
        enabled: true
      }
    })
  )
  // graph.use(
  //     new MiniMap({
  //         container: document.getElementById('container'),
  //         width: 200,
  //         height: 160,
  //         padding: 10,
  //         scalable: false,
  //     }),
  // )
  graph.use(
    new History({
      enabled: true,
      ignoreChange: true
    })
  )
  graph.use(new Export())

  let stencil
  let comNameArr = []
  let stencilJL
  const stencilContainer = document.getElementById('stencilContainer')
  new ResizeObserver(({ 0: { contentRect: { width: w } } }) => {
    if (stencilJL && Math.abs(stencilJL - w) < 100) return

    stencilJL = w
    let col = Math.floor(w / 80) || 1
    if (stencil) {
      stencilContainer.removeChild(stencil.container)
    }
    stencil = new Stencil({
      title: '全部',
      target: graph,
      placeholder: '请输入搜索词',
      notFoundText: '暂无匹配模块',
      search: searchFn,
      collapsable: true,
      stencilGraphHeight: 0,
      layoutOptions: {
        columns: col,
        // columnWidth: 'compact',    //compact
        columnWidth: 120,
        rowHeight: 80
      }
    })
    stencilContainer.appendChild(stencil.container)
    // 根据js获得数据添加并渲染
    for (const i in stGsW) {
      if (Object.hasOwnProperty.call(stGsW, i)) {
        const e = stGsW[i]
        comNameArr.push(...e.children)
        stencil.addGroup({
          name: i
          // collapsed: true     //折叠面板
        })
        let g = stencil.groups[i]
        g.setAttribute('trh', i)
        let _arr = []

        if (e.children) {
          e.children.forEach(n => {
            _arr.push(data.nodes.map(j => ({ ...j, ts: j.label, group: i })).find(m => m.label == n))
            // _arr.push(data.nodes.find(m => m.label == n))
          })
          _arr = _arr.filter(i => i)
          stencil.load(_arr, i)
          // stencil.load([..._arr, nodeNUll()], i)
        }
        if (!_arr.length) {
          g.setAttribute('null', i)
        }

        let len = Math.ceil(_arr.length / col)
        g.style.setProperty('--trh', 50 * len + 20 + 'px')
        // console.log(_arr, data.nodes.length);
      }
    }
    // console.log(data.nodes.map(i=>i.label));
  }
  ).observe(stencilContainer)

  // let b = data.nodes.map(i => i.label)
  // let c = comNameArr.filter(v => b.indexOf(v) == -1)
  // let j = b.filter(v => comNameArr.indexOf(v) == -1)
  // console.log('要做的:', comNameArr, '多做的:', j, '还未完成的：', c);

  const stencil1 = new Stencil({
    target: graph,
    placeholder: '搜索模块',
    notFoundText: '暂无匹配模块',
    search: searchFn,
    collapsable: true,
    stencilGraphHeight: 0,
    layoutOptions: {
      columns: 1,
      columnWidth: 'compact', //compact
      rowHeight: 120,
      dx: -155,
      marginY: -20
    }
  })
  const stencilDbl = document.getElementById('stencilDbl')
  stencilDbl.addEventListener('mousedown', e => e.preventDefault())
  stencilDbl.appendChild(stencil1.container)
  stencil1.addGroup({ name: 'stencilDbl' })
  let comNameArr1 = []
  for (const g in stGsW) {
    stGsW[g].children.forEach(n => {
      comNameArr1.push(data.nodes.map(i => {
        let _text = g + ' / ' + i.label
        _text = _text.length > 22 ? _text.substring(0, 22) + '...' : _text
        return {
          ...i,
          ts: i.label,
          markup: i.markup && [...i.markup, { tagName: 'text', selector: 'urlName' }],
          attrs: {
            ...i.attrs,
            urlName: {
              text: _text,
              textAnchor: 'start',
              refY: '50%',
              x: 100,
              y: -7,
              fontSize: 20
            }
          }
        }
      }).find(m => m.label == n))
    })
  }
  comNameArr1 = comNameArr1.filter((i) => i)
  comNameArr1.push(nodeNUll(522))
  // console.log(comNameArr1);
  stencil1.load(comNameArr1, 'stencilDbl')
  stencil1.groups['stencilDbl'].setAttribute('trh', 0)
  let stencilDblV = stencilDbl.querySelector('input')

  setTimeout(() => {
    let list = stencilDbl.querySelectorAll('.x6-cell.x6-node.x6-node-immovable')
    list.forEach((i) => {
      i.addEventListener('mousedown', (e) => {
        // 搜索框筛选后点击选中节点
        // console.log(123, e, dblMenu);
        dblMenu.is = false
        stencilDblV.value = null
        $('#stencilDbl .x6-widget-stencil-gr-oup.collapsable').css({ display: 'none' })
      })
    })
  }, 100)

  const KeyboardEObj = {
    'ctrl+c': () => {
      // 绑定复制事件
      const cells = graph.getSelectedCells()
      if (cells.length) {
        graph.copy(cells.map(i => { if (i.isNode()) i.attrs.body.copyId = i.uuid; return i }))
      }
      // return false
    },
    'ctrl+v': () => {
      // 绑定粘贴事件
      if (!graph.isClipboardEmpty()) {
        const cells = graph.paste({ offset: 32 })
        cells.forEach(i => { if (i.isNode()) i.copyId = i.attrs.body.copyId })
        graph.cleanSelection()
        graph.select(cells)
      }
      return false
    },
    delete: () => {
      // 监听delete
      const cells = graph.getSelectedCells()
      console.log('🤝', cells, nodeMenu.selectCells)
      // const cells = nodeMenu.selectCells
      if (cells) {
        graph.removeCells(cells)
      }
    },
    // 'ctrl+z': () => {
    //   // 监听撤销
    //   graph.undo()
    // },
    // 'ctrl+y': () => {
    //   // 监听重做
    //   graph.redo()
    // }
  }
  Object.keys(KeyboardEObj).forEach((key) => {
    graph.bindKey(key, KeyboardEObj[key])
  })



  // 监听连线完成事件
  graph.on('edge:connected', e => console.log('💂‍♀️', e))
  graph.on('edge:connected', ({
    isNew, edge, e,
    previousCell: pCell,
    currentCell: cCell,
    previousPort: ppix,
    currentPort: cpix,
    view: { routePoints }
  }) => {
    if (isNew) {
      // 对新创建的边进行插入数据库等持久化操作

      // 获取源节点和源节点连接桩的id
      ppix = edge.getSourcePortId()
      pCell = edge.getSourceCell()


      // 对被注释的节点连线后一并注释连线
      if (pCell?.attrs.display && cCell?.attrs.display) {
        edge.attr({ display: true })
        setEvent.disList.push(edge)
      }
    }
    let pp = ppix ? pCell.getPort(ppix) : null
    let cp = cpix ? cCell.getPort(cpix) : null
    let sp = edge.source.port ? edge.getSourceCell().getPort(edge.source.port) : null
    let tp = edge.target.port ? edge.getTargetCell().getPort(edge.target.port) : null

    // pp: 连接操作起始状态端，cp: 连接操作终止状态端
    // sp: 连线起始端，tp: 连线终点端

    // 非法移动起点
    let pIllegal = pp?.id != tp?.id && cp?.id == sp?.id
    // 非法移动终点
    let cIllegal = pp?.id != sp?.id && cp?.id == tp?.id
    if ((pIllegal || cIllegal) && pp) {
      pCell.setPortProp(ppix, 'attrs/path', {
        visibility: 'visible'
      })
    }

    // console.log(pIllegal, cIllegal);

    // 禁止连接输入输出端非法连接
    // console.log('pp, sp, cp, tp', pp, sp, cp, tp);
    if (sp && sp?.type != 'chu' && tp?.type == 'chu') {
      console.log(edge, pCell, ppix, cCell, cpix, edge.getTargetCell(), edge.getSourceCell(), sp, tp);
      graph.addEdge({
        source: { cell: edge.getTargetCell(), port: tp.id },
        target: { cell: edge.getSourceCell(), port: sp.id },
        attrs: {
          line: {
            targetMarker: {
              name: 'classic',
              size: 16,
              offset: 4
              // d: 'M .5 -8 16 0 .5 8 4 1 -14 1 -14 -1 4 -1 Z',
            }
          }
        }
      })
      edge.remove()
    } else if (!isNew || (sp && tp)) {
      if (sp && sp?.type != 'chu') {
        edge.remove()
        return ElMessage.warning('不能从输入端连接！')
      }
      if (tp?.type == 'chu') {
        edge.remove()
        return ElMessage.warning('不能连接输出端！')
      }
    }





    // --------连线生成后--------

    // 连接线一端以上悬空即变为红色虚线
    const kongJudge = edge.edgeGroup ? sp || tp : sp && tp
    if (kongJudge) {
      // 设置路径点
      edge.setVertices(suppress(routePoints), { silent: true })

      edge.attr({
        line: {
          stroke: '#000',
          strokeDasharray: 0
        }
      })

      if (edge.isAirEdge) {
        // 对渲染完的悬空线连接后处理
        edge.isAirEdge = false
      }



      // } else if (edge.getSourceCell()?.isEdge()) {
      //   // 三根线的逻辑
      //   let zhuEdge = edge.getSourceCell()
      //   let { source: zhuS, target: zhuT } = zhuEdge
      //   let point = edge.getSourcePoint()
      //   zhuEdge.remove()
      //   // console.log(zhuEdge, point, zhuS, zhuT, edge);
      //   // console.log(edge, pCell, cCell, ppix, cpix, sp, tp, pp, cp);
      //   graph.addEdges(getBranchEdge(point, zhuS, zhuT, pCell, cCell, edge.target.cell, edge.target.port, 1))
    } else if (edge.getTargetCell()?.isEdge()) {
      if (sp.type == 'chu') {
        edge.remove()
        return ElMessage.warning('不能从输出端连接！')
      }
      const zhuEdge = edge.getTargetCell()
      if (isAirEdge(zhuEdge)) {
        edge.remove()
        return ElMessage.warning('不能连接悬空线')
      }
      const { source: zhuS, target: zhuT } = zhuEdge, point = edge.getTargetPoint();
      console.log('🦷', zhuEdge, getBranchEdge(point, zhuS, zhuT, pCell, ppix, cCell, cpix))
      // zhuEdge.remove()
      graph.removeEdge(edge, { once: true })
      graph.removeEdge(zhuEdge, { once: true })

      // console.log(zhuEdge, point, zhuS, zhuT, edge);
      // console.log(edge, pCell, cCell, ppix, cpix, sp, tp);{x:,y:point.y}
      getBranchEdge(point, zhuS, zhuT, pCell, ppix, cCell, cpix).forEach(e => {
        const E = graph.addEdge(e)
        E.edgeGroup = zhuEdge.edgeGroup ?? zhuEdge.uuid
        if (E.target.x) {
          E.source.x ? (E.isBranch = true) : (E.isOutEdge = true)
        } else if (E.source.x) {
          E.isInEdge = true
        }
      });
      // let { routePoints: _routePoints } = graph.findViewByCell(edge)
      // console.log(_routePoints);
      // edge.setVertices(_routePoints, { silent: true })
    } else {
      edge.removeProp('vertices')

      edge.attr({
        line: {
          stroke: '#f44336',
          strokeDasharray: '10, 10'
        }
      })
    }

    // 控制箭头的显示隐藏
    // if (pp) pCell.setPortProp(ppix, 'attrs/path', {
    //   visibility: !isNew ? 'visible' : 'hidden'
    // })
    // if (cp) cCell.setPortProp(cpix, 'attrs/path', {
    //   visibility: 'hidden'
    // })

    // 设置终点箭头形态
    edge.attr({
      line: {
        targetMarker: {
          name: 'classic',
          size: 16,
          offset: 4
          // d: 'M .5 -8 16 0 .5 8 4 1 -14 1 -14 -1 4 -1 Z',
        }
      }
    })
  })
  // graph.on('edge:mousemove', (e) => {
  // console.log('👄', e)
  // })
  graph.on('cell:added', ({ cell }) => {
    // console.log(cell);
    cell.uuid = cell.uuid ?? cell.id.split('-').join('')
    cell.uid = `${cell.zIndex - 1}`
    cell.sid = 'system_' + cell.zIndex
    // console.log(cell);
  })
  graph.on('node:removed', ({ cell }) => {
    delete dialogObj[cell.uuid]
  })
  graph.on('node:added', ({
    node,
    node: {
      port: { ports },
      store: {
        data: { ts, group }
      }
    }
  }) => {
    // 为新生成的节点添加类型
    node.ts = ts
    node.group = group
    console.log('🧞', node.uid, node)

    node.removePorts()
    node.addPorts([
      ...ports.filter(m => m.type != 'chu').map((i, j) => ({ ...i, id: '_in_' + j })),
      ...ports.filter(m => m.type == 'chu').map((i, j) => ({ ...i, id: '_out_' + j }))
      // ...ports.filter(m => m.type != 'chu').map((i, j) => ({ ...i, id: (node['-ID'] ?? node.uid) + '_in_' + j })),
      // ...ports.filter(m => m.type == 'chu').map((i, j) => ({ ...i, id: (node['-ID'] ?? node.uid) + '_out_' + j }))
    ])
    let filterId = graph.defineFilter({
      name: 'dropShadow',
      args: {
        color: '#00000033',
        dx: 3,
        dy: 3
      }
    });
    let filterStr = `url(#${filterId})`
    node.attr({
      body: { filter: filterStr },
      label: { fill: '#0093FF', style: { display: 'none' } }
    })



    if (!Object.keys(nodeCount).includes(ts)) {
      nodeCount[ts] = 0
    }
    let _msg = { text: { text: ts + nodeCount[ts] } }
    if (nodeCount[ts] == 0) _msg = { text: { text: ts } }
    if (ts == 'In' || ts == 'Out') _msg.name = { text: nodeCount[ts] }
    node.attr(_msg)
    nodeCount[ts]++

    // 清除控件说明路径文字
    let ix = node.markup.findIndex((i) => i.selector == 'urlName')
    ix + 1 && node.markup.splice(ix, 1)
  })

  graph.on('edge:added', ({
    edge,
    edge: {
      target,
      source: { port: ppix }
    }
  }) => {
    // console.log(edge, target);
    if (!target.port || edge.isAirEdge) {
      let pCell = edge.getSourceCell()
      let pp = ppix ? pCell.getPort(ppix) : null
      if (pp)
        pCell.setPortProp(ppix, 'attrs/path', {
          visibility: 'hidden'
        })
      if (edge.attr('line').xu || edge.edgeGroup) return;
      console.log('👻', edge, edge.edgeGroup)
      edge.attr({
        line: {
          stroke: '#f44336',
          // strokeWidth: 1,
          strokeDasharray: '10, 10'
        }
      })
    }
  })






  graph.on('edge:mousemove', ({ cell, e: { data }, view: { cid } }) => {
    data[`__${cid}__`] && cell.removeProp('vertices')
  })
  function suppress(points, val = 0.1) {
    let len = points.length
    if (len > 1 && points[len - 1].y < points[len - 2].y) points[len - 1].y += val;
    if (len > 1 && points[len - 1].y == points[len - 2].y) points.splice(len - 2, 2);
    return points
  }
  graph.on('edge:mouseenter', ({
    edge,
    view: { routePoints },
  }) => {
    // const routePoints = graph.findView(edge).routePoints
    // console.log('👩‍👩‍👦‍👦', edge, routePoints)
    edge.setVertices(routePoints);
  })
  graph.on('edge:mouseup', ({ edge, view: { routePoints: PS } }) => {
    if (PS.length < 3) {
      return;
    }
    const getXZPS = PS => {
      for (let i in PS) {
        if (i < 2) {
          continue;
        }
        const arr = PS.slice(++i - 3, i);
        console.log('😝', arr)
        if (arr.every(j => j.x == arr[0].x || j.y == arr[0].y)) {
          return PS.filter((_, k) => k != i - 2);
        } else if (arr[0].y == arr[1].y && arr[0].x > arr[2].x) {
          return PS.filter((_, k) => k != i - 3);
        }
      }
    }
    const xz = getXZPS(PS)
    console.log('🧒', xz)
    if (xz) {
      edge.setVertices(xz)
    }
  })
  // graph.on('edge:mouseup', ({
  //   cell, view,
  //   cell: { source, target },
  //   view: { path, routePoints, sourceAnchor, targetAnchor },
  //   e: { handleObj: { namespace } },
  //   cell: { store: { previous: { tools } } }
  // }) => {
  //   if (tools && namespace != 'x6-event-v0') {
  //     if (cell.attr('line').xu) {
  //       console.log(namespace, cell, view, source);
  //       let cells = graph.getEdges().filter(i => i.attr('line').xu && i.id != cell.id)
  //       console.log(cells, cell.attr('line').xu);

  //       if (target.x) {
  //         console.log(cell.attr('line').xu, target);
  //       }
  //       if (!cells.length) return
  //       let yaoT = cells[1].target
  //       cells[1].remove()
  //       cells[0].setTarget(yaoT)
  //       cells[0].attr('line', {
  //         xu: null,
  //         targetMarker: {
  //           name: 'classic',
  //           size: 16,
  //           offset: 4
  //           // d: 'M .5 -8 16 0 .5 8 4 1 -14 1 -14 -1 4 -1 Z',
  //         }
  //       })
  //       cell.attr('line', {
  //         xu: null,
  //         sourceMarker: '',
  //         stroke: '#f44336',
  //         strokeDasharray: '10, 10'
  //       })
  //     }
  //     // let points = routePoints.map((i, j) => j ? i : { ...i, x: i.x + 100 })
  //     // let p = path.segments.map(i => i.endPoint)
  //     // let points = suppress(routePoints, [sourceAnchor, targetAnchor])
  //     let points = suppress(routePoints)
  //     cell.setVertices(points, { silent: true });
  //     // console.log(cell, view, path, sourceAnchor, targetAnchor, routePoints, points);
  //   }
  // })





  graph.on('node:move', ({ cell }) => {
    languang.id = cell.id
    languang.type = 'node'
    nodeMenu.selectCells || (nodeMenu.selectCells = [cell])
    // console.log(languang);


    // 移除所有节点之间的边上的路径点
    graph.getConnectedEdges(cell).forEach((edge) => {
      edge.removeProp('vertices')
    })
  })
  graph.on('node:moved', ({ cell }) => {
    graph.getConnectedEdges(cell).forEach((edge) => {
      let points = graph.findViewByCell(edge).routePoints
      // points = suppress(points)
      // console.log(points);
      edge.setVertices(points, { silent: true })
    })
  })




  // 移入节点添加工具
  graph.on('cell:mouseenter', ({ cell }) => {
    if (cell.isEdge()) {
      // console.log(cell.getTools());
      // if (cell.getTools()?.items.length > 0) {
      //   let [edit, ...args] = cell.getTools()?.items || []
      //   let tools = args.map(i => {
      //     i.args.attrs.hidden = false
      //     return i
      //   })
      //   console.log('加', tools);
      //   cell.setTools([edit, ...tools])
      //   return;
      // }
      // console.log(123);
      let tools = [
        'edge-editor',
        // {
        //     name: 'vertices',
        //     args: {
        //         addable: false,
        //         removable: false
        //     }
        // },
        {
          name: 'segments',
          args: {
            snapRadius: 20,
            stopPropagation: false,
            threshold: 10,
            attrs: {
              width: '100%',
              height: .05,
              x: '-50%',
              y: 0,
              rx: 0,
              ry: 0,
              // fill: '#333',
              fill: 'transparent',
              stroke: 'transparent',
              // 'stroke-width': 20,
            }
          }
        },
        {
          name: 'source-arrowhead',
          args: {
            attrs: {
              d: 'M -2.5 -10 17.5 0 -2.5 10 Z',
              fill: '#f44336',
              // d: 'M .5 -8 16 0 .5 8 4 1 -14 1 -14 -1 4 -1 Z',
              // 'stroke-width': 0,
            }
          }
        },
        {
          name: 'target-arrowhead',
          args: {
            attrs: {
              d: 'M -7.5 -10 12.5 0 -7.5 10 Z',
              fill: '#f44336'
              // d: 'M .5 -8 16 0 .5 8 4 1 -14 1 -14 -1 4 -1 Z',
              // 'stroke-width': 0,
            }
          }
        },
        // {
        //   name: 'button-remove',
        //   args: { attrs: { hidden: true } }
        // }
      ], res = { items: tools, name: '' };
      if (cell.edgeGroup) {
        if (cell.isInEdge || cell.isBranch) {
          res = { items: res.items.filter(i => i.name ? i.name != 'source-arrowhead' : i != 'source-arrowhead'), name: 'in' + res.name }
        }
        if (cell.isOutEdge || cell.isBranch) {
          res = { items: res.items.filter(i => i.name ? i.name != 'target-arrowhead' : i != 'target-arrowhead'), name: res.name + 'out' }
        }
      }
      cell.addTools(...Object.values(res))
      // console.log('🎒', res, cell.getTools())
    }
    if (cell.isNode()) {
      cell.attrs.display || cell.attrs.label?.style.display == 'none' &&
        cell.attr('label/style/display', 'initial')
    }
  })
  // 移除节点去除工具
  graph.on('cell:mouseleave', ({ cell }) => {
    if (cell.isEdge()) {
      // let [edit, ...args] = cell.getTools()?.items || []
      // let tools = args.map(i => {
      //   i.args.attrs.hidden = true
      //   return i
      // })
      // console.log('减', tools);
      // cell.setTools([edit, ...tools])
      cell.removeTools()
    }
    if (cell.isNode()) {
      if (cell.id == languang.id || languang.arr?.some(i => i.id == cell.id)) return;
      cell.attrs.label?.style.display == 'initial' && cell.attr('label/style/display', 'none')
    }
  })
  graph.on('edge:removed', ({ edge, options: { once } }) => {
    if (once) {
      return;
    }
    if (edge.edgeGroup) {
      const inGroupEdges = graph.getEdges().filter(i => i.edgeGroup == edge.edgeGroup)
      // 删除分支
      if (edge.isOutEdge) {
        nodeMenu.selectCells = inGroupEdges
      } else if (edge.isBranch) {
        nodeMenu.selectCells = [edge, ...inGroupEdges.filter(i => isEquObj(edge.target, i.source))]
      }
      languang.arr = nodeMenu.selectCells.map(i => ({ id: i.id, type: 'edge' }))
      graph.removeCells(nodeMenu.selectCells, { once: true })




      // 删除邻线时若为最后一条分支，则将上一条分支指向该节点
      const nerEdges = inGroupEdges.filter(i => isEquObj(i.source, edge.source))
      console.log('💆', edge, inGroupEdges, nerEdges)
      if (nerEdges.length == 1) {
        const fuEdge = inGroupEdges.find(i => isEquObj(i.target, edge.source))
        console.log('🧝‍♂️', fuEdge)
        if (!fuEdge) return;
        nerEdges[0].setSource(fuEdge.source)
        graph.removeEdge(fuEdge, { once: true })
        if (nerEdges[0].isInEdge) {
          delete nerEdges[0].edgeGroup
        }
        if (nerEdges[0].isBranch) {
          delete nerEdges[0].isBranch
          nerEdges[0].isOutEdge = true
        }
      }
    }
  })
  graph.on('edge:removed', ({
    edge: {
      source: { cell: pcix, port: ppix },
      target: { cell: ccix, port: cpix }
    }
  }) => {
    // 连线销毁时恢复端口
    let pCell = graph.getCellById(pcix)
    let cCell = graph.getCellById(ccix)
    let pp = ppix ? pCell?.getPort(ppix) : null
    let cp = cpix ? cCell?.getPort(cpix) : null
    // console.log(pCell, cCell, pp, cp);
    if (pp)
      pCell.setPortProp(ppix, 'attrs/path', {
        visibility: 'visible'
      })
    if (cp)
      cCell.setPortProp(cpix, 'attrs/path', {
        visibility: 'visible'
      })
  })
  // 双击进行属性改动
  graph.on('node:dblclick', ({
    cell, x, y,
    cell: {
      id, uuid, shape, port, ts,
      store: {
        data: {
          position: { x: px, y: py },
          size: { width: w, height: h }
        }
      },
      attrs: { text, name }
    }
  }) => {
    if (y >= py + h) return;


    activeName.value = 0






    cModelData(cell, x, y)
    console.log('get:', dialogObj);
    dialogObj[uuid].is = true
    // dialogObj[text.text].is = true



    const obj = {
      Scope() {
        // dialogObj[uuid].hidden = false
        // dialogObj[text.text].hidden = false
        visScopes.push(uuid)


        if (store.scopeReady) getScopeData(dialogObj[uuid].data.ScopeID, uuid)
      },
      Subsystem() {
        console.log(cell);
        // breadcrumb里arr为页面实时数据，breadcrumb里carr为页面历史数据
        if (!route.params.children) return;
        let _arr = [...route.params.children.split('>'), text.text].filter(i => i)
        if (breadcrumb.carr.every(i => i.name != text.text)) {
          const _obj = {
            name: text.text,
            level: _arr.length,
            children: _arr.join('>')
          };
          breadcrumb.carr.push(_obj)
        }
        const index = breadcrumb.carr.findIndex(i => i.name == _arr.at(-2))
        console.log('👎', index, _arr, breadcrumb.carr)
        if (index < 0) return;
        breadcrumb.carr[index].cells = graph.getCells()
        breadcrumb.arr = breadcrumb.carr.filter(i => _arr.includes(i.name))


        const cells = breadcrumb.arr.at(-1).cells ?? cell.childrenCells ?? graph.parseJSON(subSystemDefaultCells)
        console.log('👡', cells)
        // graph.resetCells(cells)
        graph.clearCells()
        graph.addCell(cells)


        const to = {
          name: 'edit',
          params: {
            children: breadcrumb.arr.at(-1).children
          },
          query: route.query
        }
        router.push(to)
      },
      ManualSwitch() {
        let on = '40,0 40,40 40,20 30,20 10,30 0,30 0,40 0,0 0,10 2,10'
        let off = '40,40 40,0 40,20 30,20 10,10 0,10 0,0 0,40 0,30 2,30'
        let status = cell.attr('switch/refPoints') == on ? 1 : 0
        dialogObj[uuid].data.CurrentSetting = status
        // dialogObj[text.text].data.CurrentSetting = status
        console.log(status)
        cell.attr('switch/refPoints', status == 1 ? off : on)
      }
    }
    obj[ts] && obj[ts]()
  })
  // 右键单击呼出菜单
  graph.on('cell:contextmenu', ({ cell, e, x, y }) => {
    console.log(nodeMenu, nodeMenu.selectCells);


    let ix1 = nodeMenu.findIndex(i => i.name == '注释节点')
    let ix2 = nodeMenu.findIndex(i => i.name == '取消注释')
    // console.log(ix1, ix2);
    ix1 + 1 && nodeMenu.splice(ix1)
    ix2 + 1 && nodeMenu.splice(ix2)


    if (nodeMenu.selectCells) {
      if (!nodeMenu.selectCells.every(i => i.attrs.display)) {
        console.log('有可被注释的节点');
        nodeMenu.find(i => i.name == '注释节点') || nodeMenu.push({
          name: '注释节点',
          icon: icons.hide,
          fn() {
            nodeMenu.selectCells.forEach(cell => {
              cell.attr({ display: true })
            })

            setEvent.disList = nodeMenu.selectCells
          }
        })
      }
      if (nodeMenu.selectCells.some(i => i.attrs.display)) {
        console.log('有取消注释的节点');
        nodeMenu.find(i => i.name == '取消注释') ||
          nodeMenu.push({
            name: '取消注释',
            icon: icons.view,
            fn() {
              let cells = nodeMenu?.selectCells.filter(i => i.attrs.display)
              // console.log('注释节点', cells)
              cells.forEach(cell => {
                cell.attr({ display: false })
              });



              setEvent.disList = graph.getCells().filter(i => i.attrs.display)
            }
          })
      }
    }





    nodeMenu.cell = cell
    menu.data = nodeMenu
    let { x: px, y: py } = graph.localToPage(x, y)
    huchu(px, py, menu)
  })
  graph.on('node:click', ({
    cell, x, y,
    cell: {
      store: {
        data: {
          position: { x: px, y: py },
          size: { width: w, height: h }
        }
      }
    }
  }) => {
    console.log('🕴', cell)
    languang.id = cell.id
    languang.type = 'node'
    nodeMenu.selectCells || (nodeMenu.selectCells = [cell])


    // console.log(x, y, px, py, w, h);
    cell.hasTool('node-editor') || cell.addTools([
      {
        name: 'node-editor',
        args: {
          x: 0,
          y: '110%'
        }
      }
    ])
    if (y < py + h) cell.removeTool('node-editor')
    // console.log(languang);
  })
  graph.on('edge:click', ({ cell, edge }) => {
    console.log('🕴', cell)
    languang.id = cell.id
    languang.type = 'edge'
    nodeMenu.selectCells = [cell]
    if (edge.edgeGroup) {
      const inGroupEdges = graph.getEdges().filter(i => i.edgeGroup == edge.edgeGroup)
      if (edge.isOutEdge) {
        nodeMenu.selectCells = inGroupEdges
      } else if (edge.isBranch) {
        nodeMenu.selectCells = [edge, ...inGroupEdges.filter(i => isEquObj(edge.target, i.source))]
      }
      languang.arr = nodeMenu.selectCells.map(i => ({ id: i.id, type: 'edge' }))
      graph.select(nodeMenu.selectCells)
    }
  })
  graph.on('blank:contextmenu', ({ e, x, y }) => {
    menu.data = blankMenu
    let { x: px, y: py } = graph.localToPage(x, y)
    huchu(px, py, menu)



    cancelDom([
      {
        id: 'dblMenu',
        ref: dblMenu,
        fn() {
          stencilDblV.value = null
          $('#stencilDbl .x6-widget-stencil-group.collapsable').css({ display: 'none' })
        }
      }
    ])
  })
  graph.on('node:mousedown', () => {
    // 取消菜单
    cancelDom([
      {
        id: 'menu',
        ref: menu
      },
      {
        id: 'nodeTem',
        ref: nodeTem.attrs?.data
      },
      {
        id: 'dblMenu',
        ref: dblMenu,
        fn() {
          stencilDblV.value = null
          $('#stencilDbl .x6-widget-stencil-group.collapsable').css({ display: 'none' })
        }
      }
    ])
  })
  graph.on('blank:dblclick', ({ e, x, y }) => {
    let { x: px, y: py } = graph.localToPage(x, y)
    // huchu(px - 10, py - 10, dblMenu)
    nextTick(() => stencilDblV.focus())
    // console.log(nodeTem)
  })
  // graph.on('cell:selected', (e) => {
  //     console.log(e);
  //     // languang.id = cell
  // })
  graph.on('blank:mousedown', ({ x, y }) => {
    languang.id = null
    languang.arr = null
    delete nodeMenu.selectCells
    // console.log(languang);

    // 获取框选区域起点
    selectArea.x = x
    selectArea.y = y

    // 取消菜单
    cancelDom([
      {
        id: 'menu',
        ref: menu
      },
      {
        id: 'nodeTem',
        ref: nodeTem.attrs?.data
      },
      {
        id: 'dblMenu',
        ref: dblMenu,
        fn() {
          stencilDblV.value = null
          $('#stencilDbl .x6-widget-stencil-group.collapsable').css({ display: 'none' })
        }
      }
    ])
  })
  graph.on('node:resizing', ({ cell, cell: { ts } }) => {
    let obj = {
      // From: ['Value'],
      // Goto: ['Value'],
      Constant: [dialogObj[cell.uuid]?.data.Value, { lue: '-C-' }],
      Gain: [dialogObj[cell.uuid]?.data.Gain, { lue: '-K-' }],
      TransferFcn: [cell.attr('latex'), { lue: '\\dfrac{num(s)}{den(s)}', isLatex: true }],
    }
    if (ts in obj) {
      compareW(cell, ...obj[ts])
    }
  })
  graph.on('blank:mouseup', ({ x, y }) => {
    let w = x - selectArea.x
    let h = y - selectArea.y
    if ((-50 < w && w < 50) || (-50 < h && h < 50)) return
    if (w > 0) {
      selectArea.w = w
    } else {
      selectArea.x = x
      selectArea.w = -w
    }
    if (h > 0) {
      selectArea.h = h
    } else {
      selectArea.y = y
      selectArea.h = -h
    }

    let nodes = graph.getNodesInArea(...Object.values(selectArea))


    // 处理边
    let cells = graph.getSubGraph(nodes, { deep: true })
    for (const node of nodes) {
      console.log('😗', graph.getConnectedEdges(node))
      cells.push(...graph.getConnectedEdges(node))
      if (graph.getConnectedEdges(node).some(i => i.isOutEdge)) {
        const edge = graph.getConnectedEdges(node).find(i => i.isOutEdge)
        const inGroupEdges = graph.getEdges().filter(i => i.edgeGroup == edge.edgeGroup)
        console.log('👇', edge, inGroupEdges)
        cells.push(...inGroupEdges)
        break;
      }
      // const edge = cells.at(-1)
      // if (edge.edgeGroup) {
      //   const inGroupEdges = graph.getEdges().filter(i => i.edgeGroup == edge.edgeGroup)
      //   if (edge.isOutEdge) {
      //     nodeMenu.selectCells = inGroupEdges
      //   } else if (edge.isBranch) {
      //     nodeMenu.selectCells = [edge, ...inGroupEdges.filter(i => isEquObj(edge.target, i.source))]
      //   }
      //   languang.arr = nodeMenu.selectCells.map(i => ({ id: i.id, type: 'edge' }))
      // }
    }
    console.log(selectArea, nodes, cells);
    if (cells) {
      nodeMenu.selectCells = cells
      graph.select(cells)
    }
    languang.arr = cells.map((i) => ({ id: i.id, type: !i.label ? 'edge' : '' }))




  })

  // 监听模型名称的改变
  watch(() => store.modelName, e => {
    breadcrumb.arr[0].name = e
    breadcrumb.carr[0].name = e
    breadcrumb.carr[0].children = e
  })
  // 监听确认按钮
  watch(() => setEvent.con, () => {
    application()
    setEvent.actDialog.is = false
  })
  // 监听帮助按钮
  watch(() => setEvent.help, () => {
    // console.log('help', con);
    let cells = graph.getCells()
    // .map((i, j) => ({ ...i, id: j, sid: 'System_' + j }))
    console.log('节点', cells, '注释的节点', setEvent.disList)
  })
  // 监听应用按钮
  watch(() => setEvent.yingyong, () => application())
  // 监听选中元素
  let oldIdArr = []
  let idArr = []
  watch(languang, ({ id, type, arr }) => {
    // console.log(id, type, arr);
    if (!arr) {
      idArr = [{ id: id, type: type }]
    } else {
      idArr = arr
    }
    if (oldIdArr.length) {
      for (const i of oldIdArr) {
        let cell = graph.getCellById(i.id)
        if (cell && !cell.attr('display')) {
          if (cell.isNode()) {
            let filterStr = cell.attr('body/filter') &&
              'url' + cell.attr('body/filter')?.split('url')
                .filter(i => i.includes('dropShadow')).join('url')
            // console.log(filterStr);
            cell.attr({
              body: { filter: filterStr },
              label: { style: { display: 'none' } }
            })
          }
          if (cell.isEdge()) {
            cell.attr({
              wrap: { stroke: 'transparent' },
              line: { stroke: '#000' }
            })
          }
        }
      }
    }

    let cell = graph.getCellById(id)
    let selCells = graph.getSelectedCells()
    let FGS = selCells.length > 1 && graph.isSelected(cell)
    let _obj = {
      From() {
        let GotoArr = (FGS ? selCells : graph.getNodes())
          .filter(i => i.ts == 'Goto' && i.attrs.data.text == cell.attrs.data.text)
        // console.log(cell, GotoArr);
        idArr = [
          ...idArr,
          ...GotoArr.map((i) => ({
            id: i.id,
            args: FGS ? {} : { color: '#c06eab' }
          }))
        ]
      },
      Goto() {
        let FromArr = (FGS ? selCells : graph.getNodes())
          .filter(i => i.ts == 'From' && i.attrs.data.text == cell.attrs.data.text)
        // console.log(cell, FromArr);
        idArr = [
          ...idArr,
          ...FromArr.map((i) => ({
            id: i.id,
            args: FGS ? {} : { color: '#c06eab' }
          }))
        ]
      }
    }
    cell && Object.keys(_obj).includes(cell.ts) && _obj[cell.ts]()

    for (const i of idArr) {
      let cell = graph.getCellById(i.id)
      if (cell && !cell.attr('display')) {
        if (i.type == 'edge') {
          cell.attr({
            wrap: { stroke: '#66C2FF', strokeWidth: 6 }
          })
          continue
        }
        let highlight = graph.defineFilter({
          name: 'highlight',
          args: {
            color: '#66C2FF',
            width: 3,
            ...i.args
          }
        })
        let filterStr = cell.attr('body/filter') + (cell.attr('body/filter')?.includes(highlight) ? '' : `url(#${highlight})`)
        cell.attr({
          body: { filter: filterStr },
          label: { style: { display: 'initial' } }
        })
      }
    }
    oldIdArr = idArr
    // console.log(id, type, idArr, oldIdArr.length);
  }, { deep: true })
  watch(() => setEvent.disList, (e, s) => {
    console.log(s, e);
    if (e.length >= s.length) {
      let cells = e
      console.log('注释节点', cells)
      cells.length && cells.forEach(cell => {
        if (cell.isNode()) {
          let contrast = `url(#${graph.defineFilter({
            name: 'contrast',
            args: {
              amount: .35
            }
          })})`
          let _str = cell.attr('body/filter')
          console.log(contrast, cell);
          cell.attr({
            body: { filter: _str.includes('contrast') ? _str : _str + contrast },
            // body: { filter: contrast },
            label: { style: { display: 'none' } }
          })
        }
        if (cell.isEdge()) {
          cell.attr({
            wrap: { stroke: '#A3BBD3', strokeWidth: 6 },
            line: { stroke: '#666' }
          })
        }
        // console.log(cell.attr());
      });
    } else {
      let cells = s.filter(i => e.indexOf(i) == -1)
      console.log('取消注释节点', cells)
      cells.forEach(cell => {
        if (cell.isNode()) {
          // let filterStr = cell.attr('body/filter') &&
          //   'url' + cell.attr('body/filter')?.split('url')
          //     .filter(i => i.includes('dropShadow')).join('url')
          // // console.log(filterStr);
          // cell.attr({ body: { filter: filterStr } })
          cell.attr({ body: { filter: '' } })
        }
        if (cell.isEdge()) {
          cell.attr({
            wrap: { stroke: '#66C2FF', strokeWidth: 0 },
            line: { stroke: '#000' }
          })
        }
      });
    }
  }, { deep: true })
  let getScopeData = (id, key) => post(`/task/describeDetail/${terminalData.value.taskId}`,
    { scopeId: id }).then(res => {
      if (!res.data.data) return ElMessage({
        message: '数据异常,请重试',
        type: 'error',
        grouping: true
      });
      if (res.data.data.status !== 40) {
        return;
      }
      let arr = res.data.data?.outcome
      console.log('%c arr ', 'font-size:13px; background:#06bf6a; color:#4affae;', arr);
      // return;
      let series = []
      for (let i = 0; i < arr[0].length - 1; i++) {
        let line = {
          type: 'line',
          symbol: 'none',
          encode: {
            x: 'x',
            y: `y${i + 1}`
          }
        }
        series.push(line, {
          ...line,
          itemStyle: { color: 'transparent' },
          xAxisIndex: 1,
          yAxisIndex: 1
        })
        // series.push()
      }
      console.log(series, arr);
      let option = {
        ...scopeOption,
        series: series,
        dataset: {
          // source: [['x', 'y1'], ...arr]
          source: [['x', 'y1', 'y2', 'y3'], ...arr]
        }
      }

      console.log('%c option ', 'font-size:13px; background:#682c9a; color:#ac70de;', option);
      // dialogObj[key].hidden = true
      dialogObj[key].is = true


      nextTick(() => {
        let scopeDom = document.getElementById(key)
        console.log('%c scopeDom ', 'font-size:13px; background:#8b3b47; color:#cf7f8b;', scopeDom);
        let scope = echarts.getInstanceByDom(scopeDom);
        if (!scope) {
          scope = echarts.init(scopeDom)
        }

        console.log('%c scope ', 'font-size:13px; background:#08b4f1; color:#4cf8ff;', scope);
        scope.setOption(option)
        rOb.observe(scopeDom.parentNode)
      })

    })
  const rOb = new ResizeObserver(e => {
    let scopeDom = [...e[0].target.childNodes].find(i => i.className == 'Scope')
    let scope = echarts.getInstanceByDom(scopeDom);
    scope.resize()
  })
  watch(() => terminalData.value, e => {
    if (e.status == 40 && e.logDetail.length == e.totalLine) {

      // console.log('🧠', visScopes)
      // for (const key of visScopes) {
      //   console.log('👢', key)

      // }


      for (const key in dialogObj) {
        if (Object.hasOwnProperty.call(dialogObj, key)) {
          const i = dialogObj[key];
          if (i.cell.ts == 'Scope' && i.is) {
            console.log(i);
            getScopeData(i.data.ScopeID, key)
          }
        }
      }
    }
  }, { deep: true })
  watch(() => breadcrumb.arr, (_, from) => {

    //     const {cells} = breadcrumb.arr.at(-1)
    // if (cells) {
    //   const 
    // }
  })
  // 监听路由变化
  router.beforeEach((to, from) => {
    if (!router.hasRoute(to.name)) return false;    // 判断路由是否有效

    // console.log('😛', to, from, breadcrumb, router.hasRoute(to.name))
    if (to.params.children && from.params.children) {   // 子系统路由逻辑
      const toArr = to.params.children.split('>')
      const fromArr = from.params.children.split('>')
      graph.zoomToFit({ padding: 50, maxScale: 1 })
      if (toArr.length >= fromArr.length) {
        // 子系统前进
        breadcrumb.arr[breadcrumb.arr.length - 1].path = to.fullPath
        breadcrumb.carr[breadcrumb.carr.length - 1].path = to.fullPath
      } else {
        // 子系统后退
        const fromIndex = breadcrumb.carr.findIndex(i => i.children == from.params.children)
        const toIndex = breadcrumb.carr.findIndex(i => i.children == to.params.children)
        console.log('👮‍♂️', fromIndex, toIndex, breadcrumb.carr)
        if (fromIndex < 0 || toIndex < 0) return;
        breadcrumb.carr[fromIndex].cells = graph.getCells()
        console.log('👨‍👧‍👦', breadcrumb.carr)
        if (breadcrumb.carr[fromIndex].children.split('>').at(-2)) {
          const n = breadcrumb.carr[fromIndex].children.split('>').at(-2)
          const cell = breadcrumb.carr.find(i => i.name == n).cells.find(i => i.label == breadcrumb.carr[fromIndex].name),
            inC = breadcrumb.carr[fromIndex].cells.filter(i => i.ts == 'In').length,
            outC = breadcrumb.carr[fromIndex].cells.filter(i => i.ts == 'Out').length
          // console.log('👍', cell, inC, outC)
          systemSetPorts(cell, inC, outC)
        }
        breadcrumb.arr = breadcrumb.carr.filter(i => toArr.includes(i.name))
        graph.resetCells(breadcrumb.carr[toIndex]?.cells)
        // graph.clearCells()
        // graph.addCell(breadcrumb.carr[toIndex]?.cells)
      }


      // console.log('👓', route, store, breadcrumb, _arr)
      // const l1 = breadcrumb.arr.length
      // const l2 = breadcrumb.carr.length
      // if (l1 < l2) {
      //   breadcrumb.arr = breadcrumb.carr.push(0, l1 + 2)
      // } else {
      //   breadcrumb.carr.push({
      //     name: _arr.at(-1),
      //     level: _arr.length,
      //     children: _arr.join('>')
      //   })
      //   if (breadcrumb.carr.at(-1).children == breadcrumb.carr.at(-2).children) breadcrumb.carr.pop()
      //   breadcrumb.arr = breadcrumb.carr
      // }
      // const to = {
      //   name: 'edit',
      //   params: {
      //     children: breadcrumb.arr.at(-1).children
      //   },
      //   query: route.query
      // }
      // router.push(to)
    }
  })

  // 检索本地缓存，初始化数据(在事件监听后)
  if (store.graphALLCell) {
    graph.resetCells(store.graphALLCell)
  }
  if (store.svg && store.SubsystemId) {
    let cell = graph.getCellById(store.SubsystemId)
    let prop = cell.prop()
    let {
      attrs,
      attrs: { img }
    } = prop
    // console.log(prop);
    let _set = {
      ...prop,
      width: 200,
      height: 120,
      attrs: {
        ...attrs,
        img: {
          ...img,
          href: store.svg
          // 'xlink:href': store.svg,
        }
      }
    }
    // console.log(_set);
    cell.remove()
    cell = graph.addNode(_set)
  }










  breadcrumbInit()



  // 终端滚动动画实现
  new MutationObserver(({ 0: { target: div } }) => {
    let { clientHeight: ch, scrollHeight: sh } = div
    let _len = sh - ch
    if (_len) {
      div.scrollTo({
        top: _len,
        behavior: 'smooth'
      });
    }
    // console.log(div, _len);
  }).observe($('#terminal div')[0], { childList: true })
})
const fz_getEndJson = async () => {
  console.log('🤢', breadcrumb, breadcrumb.carr[0], breadcrumb.carr[0].cells)
  let cells = graph.getCells()
  if (breadcrumb.arr.length > 1) {
    cells = breadcrumb.carr[0].cells
  }
  console.log('🧟', cells)
  let nodes = cells.filter(i => i.isNode())
  if (!nodes.length) {
    return ElMessage.warning('请先添加节点')
  }
  let edges = cells.filter(i => i.isEdge())
  const _json = getEndJson(glProps.data, nodes, edges)

  console.log('😑', dialogObj, Object.keys(dialogObj).length, _json)
  const _data = { modelData: _json, name: store.modelName, id: store.SavedInformationId }
  if (!await netSave(_data)) return;
  return _json
}
const topMenufnS = reactive([
  {
    name: '新建',
    fn: (e) => {
      console.log('新建', e)
    }
  },
  {
    name: '打开',
    fn: (e) => {
    }
  },
  {
    name: '保存',
    fn: async (e) => {
      let arr = [
        () => post('/model-info/clearAll').then(({ data }) => {
          data.code == 200 && ElMessage.success('清空成功')
        })
      ]

      if (e in arr) arr[e](); else {
        const _json = await fz_getEndJson()
        if (!_json) return;

      }
    }
  },
  {
    name: '打印',
    fn: (e) => {
      console.log('打印', graph.toJSON())
    }
  },
  {
    name: '步退',
    fn: (e) => {
      console.log('步退', e)
    }
  },
  {
    name: '运行',
    fn: async (e) => {
      store.scopeReady = true

      terminalData.value = { logDetail: [], stepLength: 5 }
      glProps.progress.p = 0
      delete glProps.progress.s

      const _json = await fz_getEndJson()
      if (!_json) return;
      // return console.log('%c _json ', 'font-size:13px; background:#a73b65; color:#eb7fa9;', _json);
      let arr = [
        () => {
          axios.request({
            url: 'http://101.6.219.33:8080/convert/v3/json2xml',
            method: 'post',
            responseType: 'blob',
            data: _json
          }).then(res => {
            let blob = new Blob([res.data], { type: 'application/zip' })
            console.log(res, blob);
            const fileName = 'data.zip'
            if ('download' in document.createElement('a')) { // 非IE下载
              const elink = document.createElement('a')
              elink.download = fileName
              elink.style.display = 'none'
              elink.href = URL.createObjectURL(blob)
              // console.log(elink);
              // if (elink.href.split(':')[1] == 'http') {
              //   elink.href = elink.href.replace('http', 'https')
              // }
              document.body.appendChild(elink)
              elink.click()
              URL.revokeObjectURL(elink.href) // 释放URL 对象
              document.body.removeChild(elink)
            }
          })
        }
      ]

      if (e in arr) arr[e](); else {
        console.log('%c _json ', 'font-size:13px; background:#ab4970; color:#ef8db4;', _json);
        let index = 0
        let ReadLogs = (id) => pollFn(async () => {
          let { data } = await post('task/showLogDetail', {
            "startLine": terminalData.value.logDetail.length,
            "stepLength": terminalData.value.stepLength,
            "taskId": id
          })
          console.log(`%c 轮询中，正在进行第${++index}次轮询... `, 'font-size:13px; background:#ab4970; color:#ef8db4;');
          if (!data || data?.code != 200) {
            terminalData.value.status = 50;
            return ElMessage.error('获取日志失败');
          }
          terminalData.value = {
            ...data.data,
            logDetail: [
              ...terminalData.value.logDetail,
              ...data.data.logDetail
            ].map(i => ({ ...i, time: i.time.slice(-8) })),
          }

          let { startLine, stepLength, totalLine, status } = terminalData.value
          if ([40, 50, 60].includes(status)) {
            // console.log(stepLength, status, terminalData.value);
            terminalData.value.stepLength = 999
            if (startLine + stepLength >= totalLine || totalLine === 0) {
              console.log('%c 轮询结束，terminalData： ', 'font-size:13px; background:#0a8cf9; color:#4ed0ff;', terminalData.value);
              return ElMessage.info('没有更多数据了');
            }
          }
        }, { timeout: 1000 * 9999 })
        let DescribeTask = (id) => pollFn(async () => {
          // topMenufnS[5].disabled = true
          if (!glProps.progress.p) {
            pollFn(() => {
              if (glProps.progress.s) {
                if (glProps.progress.s == 'discontinue') {
                  return glProps.progress.p = 0;
                }
                return glProps.progress.p = 100;
              } else if (glProps.progress.p == 90) {
                return glProps.progress.p = 90;
              } else {
                glProps.progress.p += Math.floor(Math.random() * 4)
              }
            }, { interval: 200 })
          }

          if (glProps.progress.s) {
            return true;
          }
          let { data: { data } } = await get(`/task/describe/${id}`)
          // if (!data || data?.code != 200) return true;
          console.log(data);
          let statusObj = { 40: 'success', 50: 'exception', 60: 'discontinue' }
          // if (data.status == 40 || data.status == 50) {
          if (data.status in statusObj) {
            // topMenufnS[5].disabled = false
            // terminalData.value.stepLength = 999
            setTimeout(() => {
              glProps.progress.s = statusObj[data.status]
            }, 800);
          }
        }, { timeout: 1000 * 9999 })
        let { data: { data: taskId } } = await post('/task/add', { modelId: store.SavedInformationId })
        ReadLogs(taskId)
        DescribeTask(taskId)
      }
    }
  },
  {
    name: '步进',
    fn: (e) => {
      console.log('步进', e)
    }
  },
  {
    name: '停止',
    fn: (e) => {
      // console.log('停止', e)
      // console.log('👷‍♀️', terminalData.value, store)
      if (!terminalData.value.taskId) {
        return;
      }
      post('/task/stop/' + terminalData.value.taskId).then(({ data }) => {
        data.code == 200 && ElMessage.success('任务被取消')
      })
    }
  }
])

function huchu(x, y, ref) {
  ref.x = x
  ref.y = y
  ref.is = true
}
let nodeMenu = [
  {
    name: '删除节点',
    icon: icons.delete,
    fn() {
      let cell = nodeMenu.cell
      console.log('删除节点', cell.prop())
      cell.remove()
      // graph.removeCells(graph.getSelectedCells())
      // console.log('👯', graph.getSelectedCells(), nodeMenu.selectCells)
      // KeyboardEObj.delete()
    }
  },
  // {
  // name: '左右翻转',
  // icon: icons.delete,
  // fn() {
  // console.log(nodeMenu);
  // let nodes = nodeMenu.selectCells
  // if (!nodeMenu.selectCells) {
  //   nodes = [nodeMenu.cell]
  //   console.log('单控件翻转', nodeMenu.cell);
  // }
  // // 翻转框选组件
  // let overturn = graph.getCellsBBox(nodes)
  // let zhong = overturn.x + overturn.width / 2
  // console.log(overturn, zhong);
  // for (const i of nodes) {
  //   if (i.isEdge()) {
  //     continue
  //   }
  //   let { position: { x, y }, size: { width: w, height: h } } = i.prop()
  //   console.log(i, i.prop(), x, y, w, h, i.getAngle(), i.attr('body/transform'));
  //   i.prop({ position: { x: zhong * 2 - x - w } })
  //   i.attr('body/transform', i.attr('body/transform')?.includes('rotate') ? '' : `rotate(180,${w / 2},${h / 2})`)
  //   i.prop('ports/items').forEach((j, index) => {
  //     let str1 = 'scale(0.015) translate(-510,-850)'
  //     let str2 = 'scale(0.015) translate(510,350) rotate(180)'
  //     // let str = j.attrs.path.transform + ' rotate(180)'
  //     // console.log(j, index, str);
  //     i.prop(`ports/items/${index}/group`, i.prop(`ports/items/${index}/group`) == 'right' ? 'left' : 'right')
  //     i.prop(`ports/items/${index}/attrs/path/transform`,
  //       i.prop(`ports/items/${index}/attrs/path/transform`).includes('rotate') ? str1 : str2)
  //   });
  // }
  // }
  // }
]
let blankMenu = [
  {
    name: '功能按钮1',
    icon: icons.copy,
    fn() {
      console.log('功能按钮1')
    }
  },
  {
    name: '功能按钮2',
    icon: icons.add,
    fn() {
      console.log('功能按钮2')
    }
  },
  {
    name: '功能按钮3',
    icon: icons.delete,
    fn() {
      console.log('功能按钮3')
    }
  }
]
const menu = reactive({
  is: false,
  x: 0,
  y: 0,
  data: null
})
const dblMenu = reactive({
  is: false,
  x: 0,
  y: 0,
  data: null
})



function queryType(i) {
  if (isNum(i) || isExp(i)) return "F64"
  // if (isArr(i, i => isNum(i) || isExp(i))) {
  if (isArr(i)) {
    if (typeof i === 'string') {
      i = i.slice(1, -1).split(',')
    }
    return "F64_" + i.length
  }
  return "STR"
}

// let res = {}
// for (const i of Object.keys(aaaaaaaaaaa)) {
//   const e = aaaaaaaaaaa[i];
//   res[i] = {}
//   for (const j of Object.keys(e)) {
//     const f = e[j];
//     // res[i][j] = f
//     res[i][j] = queryType(f)
//   }
// }
// const defaultDataType = { "Constant": { "Value": "F64", "VectorParams1D": "STR", "SampleTime": "STR" }, "SineWave": { "SineType": "STR", "Time": "STR", "Amplitude": "F64", "Bias": "F64", "Frequency": "F64", "Phase": "F64", "SamplesPerPeriod": "F64", "VectorParams1D": "STR" }, "Step": { "Time": "F64", "Before": "F64", "After": "F64", "SampleTime": "F64", "VectorParams1D": "STR", "ZeroCross": "STR" }, "Sign": { "ZeroCross": "STR" }, "Sum": { "Inputs": "STR" }, "Abs": { "ZeroCross": "STR" }, "Delay": { "DelayLength": "F64", "InitialLength": "F64", "InputProcessing": "STR", "UseCircularBuffer": "STR", "ShowEnablePort": "STR", "ExternalReset": "STR", "SampleTime": "F64" }, "DeadZone": { "UpperValue": "F64", "LowerValue": "F64", "SaturateOnIntegerOverflow": "STR", "LinearizeAsGain": "STR", "ZeroCross": "STR" }, "Gain": { "Gain": "F64" }, "Lookup_1D": { "NumberOfTableDimensions": "F64", "X_VEC": "F64_3", "Y_VEC": "F64_3", "InternalRulePriority": "STR" }, "Math": { "Operator": "STR" }, "MinMax": { "Function": "STR", "Inputs": "F64", "ZeroCross": "STR" }, "Product": { "Inputs": "STR" }, "Saturation": { "UpperValue": "F64", "LowerValue": "F64", "LinearizeAsGain": "STR", "ZeroCross": "STR" }, "Sqrt": { "Operator": "STR" }, "RateLimiter": { "RisingSlewLimit": "F64", "FallingSlewLimit": "F64", "SampleTimeMode": "STR", "InitialCondition": "F64", "LinearizeAsGain": "STR" }, "RelationalOperator": { "Operator": "STR", "ZeroCross": "STR" }, "Integrator": { "ExternalReset": "STR", "InitialConditionSource": "STR", "InitialCondition": "F64", "LimitOutput": "STR", "UpperSaturationLimit": "STR", "LowerSaturationLimit": "STR", "ShowSaturationPort": "STR", "AbsoluteTolerance": "STR", "IgnoreLimit": "STR", "ZeroCross": "STR", "StateName": "STR" }, "TransferFcn": { "Numerator": "STR", "Denominator": "STR", "InitialCondition": "F64", "ParameterTunability": "STR", "AbsoluteTolerance": "STR", "ContinuousStateAttributes": "STR" }, "PID": { "Controller": "STR", "P": "F64", "I": "F64", "D": "F64", "UseFilter": "STR", "N": "F64", "LimitOutput": "STR", "InitialConditionForIntegrator": "F64", "InitialConditionForFilter": "F64", "UpperSaturationLimit": "STR", "LowerSaturationLimit": "STR", "IgnoreLimit": "STR", "ZeroCross": "STR", "StateName": "STR" }, "GainSchedule": { "Controller": "STR", "X_VEC": "F64_2", "P_VEC": "F64_2", "I_VEC": "F64_2", "D_VEC": "F64_2", "UseFilter": "STR", "N": "F64", "LimitOutput": "STR", "InitialConditionForIntegrator": "F64", "InitialConditionForFilter": "F64", "UpperSaturationLimit": "STR", "LowerSaturationLimit": "STR", "IgnoreLimit": "STR", "ZeroCross": "STR", "StateName": "STR" }, "Acc_Control": { "X_VEC": "F64", "Wf_VEC": "F64" }, "Filter_1st": { "a": "F64", "InitialCondition": "F64", "TimeStep": "F64" }, "Filter_2nd": { "ParameterMode": "STR", "ksi": "F64", "omega": "F64", "a": "F64", "b": "F64", "InitialCondition": "F64", "TimeStep": "F64" }, "Filter_RateLimit": { "a": "F64", "InitialCondition": "F64", "RateUpperValue": "F64", "RateLowerValue": "F64", "TimeStep": "F64" }, "Switch": { "Criteria": "STR", "Threshold": "F64", "ZeroCross": "STR" }, "ManualSwitch": { "CurrentSetting": "F64" }, "Mux": { "Inputs": "F64", "SampleTimeMode": "STR" }, "Demux": { "Outputs": "F64", "SampleTimeMode": "STR" }, "DllModel": { "Path": "STR", "InitialCondition": "F64_2", "InitFunc": "STR", "OnestepFunc": "STR", "TerminateFunc": "STR", "NumofInput": "F64", "NumofOutput": "F64" }, "FMU": { "Path": "STR", "InitialCondition": "F64", "TimeStep": "F64" }, "In": { "Port": "F64" }, "Out": { "Port": "F64" }, "Goto": { "GotoTag": "STR" }, "From": { "GotoTag": "STR" } }




function getEndJson(config, nodes, edges) {
  console.log(config, nodes, edges, dialogObj);



  function _getSystem(edges, nodes) {
    console.log('😃', edges, nodes)
    let e = edges.map(i => {
      // console.log(i);
      let inNode = i.getTargetCell(), outNode = i.getSourceCell(), souP = i.source.port,
        tarP = i.target.port, inPortIndex, outPortIndex, res = {};

      if (i.edgeGroup) {
        if (!i.isOutEdge) return;
        const getBranch = (e, obj = { _point: e.target, _branch: [] }) => {
          const childEdges = edges.filter(f => f.edgeGroup == e.edgeGroup &&
            f.source.x == e.target.x && f.source.y == e.target.y)
          for (const edge of childEdges) {
            const vertices = (graph.findView(edge)?.routePoints ?? edge.vertices).map(i => ({ x: i.x, y: i.y }));
            console.log('😨', edge, vertices)
            if (edge.isInEdge) {
              obj._branch.push({
                inNodeId: edge.getTargetCell().uid,
                inPortIndex: edge.target.port.split('_in_')[1],
                vertices: vertices
              })
            }
            if (edge.isBranch) {
              obj._branch.push(getBranch(edge, { _point: edge.target, _branch: [], vertices: vertices }))
            }
          }
          return obj
        }
        const branch = getBranch(i)
        console.log('👩‍🚒', i, branch)
        res = {
          edgeGroup: i.edgeGroup,
          ...branch
        }
      }


      const vertices = (graph.findView(i)?.routePoints ?? i.vertices).map(i => ({ x: i.x, y: i.y }));

      if (!i.edgeGroup && (!inNode || !outNode)) {
        console.log('😶', i, inNode, tarP, outNode, souP)
        let fanzhen;
        res.isAirEdge = true
        if (souP && /in/.test(souP)) {
          tarP = souP
          inNode = outNode
          outNode = null
          fanzhen = i.target
          res.vertices = vertices.toReversed()
        }
        if (!inNode) {
          res.inNodePoint = i.target
        }
        if (!outNode) {
          res.outNodePoint = fanzhen ?? i.source
        }
        console.log('😶111111111', i, inNode, tarP, outNode, souP)
      }

      console.log('%c souP tarP ', 'font-size:13px; background:#95f28c; color:#d9ffd0;', souP, tarP, i.id);
      // if (inNode) inPortIndex = inNode.port.ports.filter(j => !j.type).findIndex(j => j.id == tarP)
      // if (outNode) outPortIndex = outNode.port.ports.filter(j => j.type == 'chu').findIndex(j => j.id == souP)
      if (inNode) inPortIndex = tarP.split('_in_')[1]
      if (outNode) outPortIndex = souP.split('_out_')[1]
      console.log('🕶', outPortIndex, inPortIndex)



      return {
        "inNodeId": inNode?.uid,
        "inPortIndex": inPortIndex,
        "outNodeId": outNode?.uid,
        "outPortIndex": outPortIndex,
        vertices: vertices,
        ...res
      }
    }).filter(i => i)
    let n = nodes.map(i => {
      let portCArr = i.port.ports.filter(j => j.type == 'chu'), childrenSystem;
      let portRArr = i.port.ports.filter(j => !j.type)
      let portCC = portCArr.length
      let portRC = i.port.ports.length - portCC
      const position = i.position()
      const size = i.size()
      cModelData(i)
      if (i.copyId && i.ts != 'Scope') dialogObj[i.uuid].data = dialogObj[i.copyId].dat


      let internalData = {
        "caption": i.label,
        "input-port": {
          // "-port": portRArr.map(j => j.attrs.text?.text || '').join('|')
          "-port": portRArr.map(j => j.attrs.text?.value || j.attrs.text?.text || '').join('|')
        },
        "node-attr": {
          "attr": Object.entries(dialogObj[i.uuid]?.data || [])
            // "attr": Object.entries(dialogObj[i.label]?.data || [])
            .map(j => {
              return {
                "-Name": j[0],
                "-DataType": queryType(j[1]),
                // "-DataType": defaultDataType[i.ts][j[0]],
                "#text": Array.isArray(j[1]) ? '[' + j[1] + ']' : j[1]
              }
            })
        },
        "output-port": {
          // "-port": portCArr.map(j => j.attrs.text?.text || '').join('|')
          "-port": portCArr.map(j => j.attrs.text?.value || j.attrs.text?.text || '').join('|')
        }
      }
      if (i.ts == "Subsystem") {
        const index = breadcrumb.carr.findIndex(j => j.name == i.label)
        console.log('😠', index)
        let cells;
        if (index + 1) {
          cells = breadcrumb.carr[index]?.cells ?? graph.getCells()
        } else {
          cells = i.childrenCells ?? graph.parseJSON(subSystemDefaultCells).map((j, jindex) => {
            j.group = 'Subsystem'
            j.ts = j.label
            j.sid = 'system_' + (jindex + 1)
            j.uid = (jindex + 1) + ''
            j.uuid = j.id.split('-').join('')
            return j
          })
        }
        console.log('🗣111111', i, breadcrumb, index, cells)
        const e = cells.filter(i => i.isEdge())
        const n = cells.filter(i => i.isNode())
        childrenSystem = _getSystem(e, n)
        internalData = {
          ...internalData,
          "childrenSystem": childrenSystem
        }
      }
      // console.log('👯‍♂️', i, i['-SID'], i.sid)





      const supplement = { _customTemplate: null };
      if (Object.keys(supplementNodes).includes(i.ts)) {
        supplement._customTemplate = getMObjVal(supplementNodes[i.ts], dialogObj[i.uuid])
        console.log('🙎', i, supplement, supplementNodes[i.ts], dialogObj)
      };







      return {
        uuid: i.uuid,
        "-Ports":
          // i['-Ports'] ?? 
          `[${portRC},${portCC}]`,
        "-SID": i['-SID'] ?? i.sid,
        "-NodeType": i['-NodeType'] ?? `${i.group}/${i.ts}`,
        "-ID":
          // i['-ID'] ??
          (store.reStart ? store.reStart + i.uid : i.uid),
        "Commented": i.attrs.display ? "on" : "",
        "internal-data": internalData
        //  ?? i['internal-data']
        ,
        "position": position,
        "size": size,
        ...supplement
      }
    })
    return { connections: e, nodes: n }
  }


  let _obj = {
    "System": {
      config: config,
      ..._getSystem(edges, nodes)
    }
  }



  // return JSON.stringify(objV2S(_obj), null, 4)
  return objV2S(_obj)
}




let type2color = (type) => {
  let arr = ['log', 'info', 'success', 'error',]
  return arr[type - 1]
}
const terminalData = ref({
  logDetail: [],
  stepLength: 5,
})






let netSave = async (_data) => {
  if (Object.values(_data.modelData.System.config).some(i => !i)) {
    return ElMessage.error('模型配置缺失,请检查停止时间和步长');
  }
  let { data } = await post('/model-info/edit', _data)
  if (!data || data.code != 200) return;
  store.SavedInformationId = data.data
  store.saveStatus = 1
  ElMessage.success('id为：' + data.data + ' 的模型保存成功')
  return data
}
// // 防数据丢失
// onMounted(() => {
//   if (!store.saveStatus) {
//     ElMessageBox.confirm('检测到未保存数据，是否恢复？', '提示', {
//       confirmButtonText: '确定',
//       cancelButtonText: '取消',
//       type: 'warning'
//     }).then(async () => {
//       if (store.saveStatus == 1) {
//         console.log('恢复数据');
//       }



//       await reOpen()
//     })
//   }
//   store.saveStatus = -1
// })


function application(dl = setEvent.actDialog) {
  let {
    cell, data, config,
    cell: {
      uuid, store: {
        data: { size, size: { width, height } }
      }
    },
    illustrate: { id, name, type }
  } = dl
  // dl.finalData = data



  console.log(cell, dl, dialogObj[uuid])


  let conObj = {
    From() {
      if (!config) {
        cell.attr('data/text', data.GotoTag)
        return;
      }
      let { mark, GotoArr } = config
      let gotoCell = GotoArr.find((i) => i.label == mark)
      if (gotoCell) {
        // console.log(mark, gotoCell);
        let {
          attrs: {
            name: { value, text }
          }
        } = gotoCell
        let gotoMark = (value || text).slice(1, -1)
        // console.log(gotoMark);
        compareW(cell, gotoMark)
      }
      // console.log(data, cell, mark, id);
    },
    Goto() {
      if (!config) {
        cell.attr('data/text', data.GotoTag)
        return;
      }
      let { mark } = config
      compareW(cell, mark)
    },
    Mux() {
      if (!data.Inputs) return ElMessage.warning('请输入正确的数量')
      console.log('😨', data)
      if (isNum(data.Inputs)) {
        systemSetPorts(cell, +data.Inputs, 1)
      } else if (/^[\[].*[\]]$/.test(data.Inputs) && data.Inputs.slice(1, -1).split(',').every(i => isNum(i) && +i % 1 == 0)) {
        systemSetPorts(cell, data.Inputs.slice(1, -1).split(',').length, 1)
      } else return ElMessage.error('请输入正确的格式');
    },
    Demux() {
      if (!data.Outputs) return ElMessage.warning('请输入正确的数量')
      console.log('👱‍♀️', data)
      if (isNum(data.Outputs)) {
        systemSetPorts(cell, 1, +data.Outputs)
      } else if (/^[\[].*[\]]$/.test(data.Outputs) && data.Outputs.slice(1, -1).split(',').every(i => isNum(i) && +i % 1 == 0)) {
        systemSetPorts(cell, 1, data.Outputs.slice(1, -1).split(',').length)
      } else return ElMessage.error('请输入正确的格式');
    },
    Constant() {
      compareW(cell, data.Value, { lue: '-C-' })
    },
    Sum() {
      console.log('💆‍♀️', name, cell, data)
      // console.log(data.Inputs);
      // const left = Inputs2Port(data.Inputs, 'circle').map((i, j) => ({ ...i, id: cell['-ID'] + '_in_' + j }))
      // const right = Inputs2Port('=', 'circle').map((i, j) => ({ ...i, id: cell['-ID'] + '_out_' + j }))
      // // console.log('🤴', left, right)
      // cell.removePorts()
      // cell.prop('ports/items', [...left, ...right])
      // console.log(cell.prop());
      const ports = data.Inputs
      // .split('').reverse().join('')
      systemSetPorts(cell, ports, ' ', 'circle')
    },
    Scope() {
      // if (!data.Inputs) return ElMessage.warning('请输入正确的数量')
      systemSetPorts(cell, +data.Inputs, 0)
    },
    MinMax() {
      if (!data.Inputs) return ElMessage.warning('请输入正确的数量')
      systemSetPorts(cell, +data.Inputs, 1)
    },
    Gain() {
      compareW(cell, data.Gain, { lue: '-K-' })
    },
    TransferFcn() {
      let { Numerator: fz, Denominator: fm } = data, fzl, fml;
      if (isNum(fz)) fz = `[${fz}]`, fzl = fz;
      if (isNum(fm)) fm = `[${fm}]`, fml = fm;
      fz = fz.slice(1, -1).split(',')
      fm = fm.slice(1, -1).split(',')
      console.log('💑', fz, fm, dialogObj[uuid])
      if (fz.some(i => !i) || fm.some(i => !i) || fz.length > fm.length) return ElMessage.error('请检查输入格式')
      if (!fzl) fzl = fz.length == 1 ? fz[0] : fz.map((i, j, a) => {
        const m = a.length - j - 1
        if (!m) return i
        const s = m == 1 ? 's' : `s^{${m}}`
        return +i == 1 ? s : i + s
      }).join('+')
      if (!fml) fml = fm.length == 1 ? fm[0] : fm.map((i, j, a) => {
        const m = a.length - j - 1
        if (!m) return i
        const s = m == 1 ? 's' : `s^{${m}}`
        return +i == 1 ? s : i + s
      }).join('+')
      console.log('👎', fml, fzl, fzl.replaceAll(/[\{\^\}]/g, ''), fml.replaceAll(/[\{\^\}]/g, ''))
      const latex = getLatex({ latex: `\\dfrac{${fzl}}{${fml}}` })
      console.log(latex, cell);
      const sw = Math.max(
        fzl.replaceAll(/[\{\^\}]/g, '').length + fz.length - 1,
        fml.replaceAll(/[\{\^\}]/g, '').length + fm.length - 1
      ) * 8
      cell.attr({
        latex: {
          html: latex,
          latex: `\\dfrac{${fzl}}{${fml}}`,
          htmlW: sw,
          x: -sw / 2 + 2.5
        }
      })




      // console.log('💩', sw)
      cell.size(sw + 20, height)
    },
    Product() {
      if (!data.Inputs) return ElMessage.warning('请输入正确的数量')
      console.log('👺', cell, data)
      systemSetPorts(cell, data.Inputs, 1)
    },
    Integrator() {
      if (data.LimitOutput) {
        cell.attr('img/href', { off: '/img/58.webp', on: '/img/5.webp' }[data.LimitOutput]);
      }
      if (!config) return;
      console.log('🤐', config.InitialConditionSourceOption, data.InitialConditionSource)
      const IntegratorPorts = [
        '',
        config.ExternalResetOption[data.ExternalReset][1],
        config.InitialConditionSourceOption[data.InitialConditionSource][1]
      ].filter(i => i != null)
      console.log('🙅‍♂️', IntegratorPorts)

      systemSetPorts(cell, IntegratorPorts, 1)
    },
    FMU_Model() {
      console.log('👑', data, config)
      if (!config.fmu) {
        return;
      }
      const { png, input, output, title } = config.fmu
      if (png) {
        console.log('🙁', input, output, title)
        cell.attr('url/html', null)
        cell.attr('name/html', null)
        cell.attr('img/href', `data:image/png;base64,${png}`)
        cell.size(200, 100)
      } else {
        compareW(cell, title, { url: 'url/html', isHtml: true, lue: title })
        compareW(cell, 'Import', { url: 'name/html', isHtml: true, lue: 'Import' })
        cell.attr('img/href', null)
        // compareW(cell, title, { url: 'name/html' })
      }
      systemSetPorts(cell, input, output)
    },
  }


  Object.keys(conObj).includes(type) && conObj[type]()
  console.log('🧟', dialogObj)
  // console.log('最终处理过的数据', dl.data)
  // console.log('最终处理过的数据', dl.finalData)
}
const supplementNodes = {
  FMU_Model: 'config/fmu'
};
async function reOpen() {
  const { data: { data: { modelData: _json, name } } } =
    await post('/model-info/info/' + store.SavedInformationId)
  console.log('👬', _json, name)
  store.modelName = name
  store.SavedInformationData = { name: name, json: _json }
  if (!Object.keys(_json).length) return;
  const _data = Json2Antv(_json)

  function cCells2Arr(cells, arr = []) {
    for (const i in cells) {
      if (Object.hasOwnProperty.call(cells, i)) {
        const e = cells[i];
        if (e.childrenCells) {
          const inC = e.childrenCells.filter(i => i.ts == 'In').length,
            outC = e.childrenCells.filter(i => i.ts == 'Out').length
          systemSetPorts(e, inC, outC)
          cCells2Arr(e.childrenCells, arr)
        } else if (e.isNode()) {
          arr.push(e)
        }
      }
    }
    arr = [...arr, ...cells.filter(i => i.isNode())]
    return [...new Set(arr)]
  }
  console.log('👱‍♀️', _data, cCells2Arr(_data))

  let reStart = 0;
  cCells2Arr(_data).forEach(i => {
    if (i['-ID'] && reStart <= +i['-ID']) reStart = +i['-ID'];
    // console.log(i, i.uuid);
    const { caption, 'node-attr': { attr } } = i['internal-data']
    if (!attr) console.log('😻', caption, attr, i)
    let obj = {};
    (attr || []).forEach(j => {
      obj[j['-Name']] = isNum(j['#text']) ? +j['#text'] : j['#text']
    })
    // console.log('😘', obj, i)
    dialogObj[i.uuid] = {
      // dialogObj[caption] = {
      cell: i, data: obj,
      // illustrate: { id: i.uuid, name: i.ts }
      // illustrate: { id: i.attrs.text.text, name: i.ts }
    }
    if (Object.keys(supplementNodes).includes(i.ts)) {
      setMObjVal(supplementNodes[i.ts], i._customTemplate, dialogObj[i.uuid])
      // console.log('👩‍🏭', dialogObj[i.uuid])
    };
    cModelData(i)
    application(dialogObj[i.uuid])
  })
  if (!reStart) store.reStart = reStart
  graph.clearCells()
  graph.addCell(_data)
  const fitZoom = graph.size.options.width / graph.getContentArea().width
  if (fitZoom <= 1) {
    graph.zoomTo(fitZoom - 0.01)
    graph.positionContent('top')
    const { tx, ty } = graph.translate()
    graph.translate(tx, ty + 20)
  }

  console.log(dialogObj);
}
// window.addEventListener('beforeunload', e => {
//   store.buloadTime = new Date()
//   // e.preventDefault()
// });
// window.addEventListener('unload', e => {
//   let timeDiff = new Date() - store.buloadTime
//   if (timeDiff <= 5 && store.saveStatus == -1) store.saveStatus++
// })
</script>
<template>
  <!-- <RouterView /> -->

  <el-container style="width: max(100%, 1200px); height: 100vh">
    <el-header :height="glProps.topH + ''">
      <div id="topMenu">
        <TopMenu :fns="topMenufnS" :attr="{
          stencil: glProps.stencil,
          progress: glProps.progress,
          data: glProps.data
        }" />
      </div>
    </el-header>
    <el-container class="containerGap">
      <SplitBox direction="right" id="stencilId" :w="glProps.gap">
        <el-aside id="stencilId" :width="glProps.stencil.w + 'px'" v-show="glProps.stencil.is">
          <div id="stencilContainer" />
        </el-aside>
      </SplitBox>
      <el-container>
        <el-main>
          <el-breadcrumb :separator-icon="ArrowRight">
            <template v-for="im in breadcrumb.arr">
              <el-breadcrumb-item :to="im.path">
                {{ im.name }}
              </el-breadcrumb-item>
            </template>
          </el-breadcrumb>
          <div id="container" />
          <!-- 终端按钮 -->
          <div class="terminalBtn flex-cc"
            @click="glProps.terminal.is = !glProps.terminal.is, console.log(glProps.terminal.is)">
            <el-icon color="#FFF">
              <ArrowDownBold v-if="glProps.terminal.is" />
              <ArrowUpBold v-else />
            </el-icon>
          </div>

          <!-- 工作台按钮 -->
          <div class="glIdBtn flex-cc" @click="glProps.right.is = !glProps.right.is, console.log(glProps.right.is)">
            <el-icon color="#FFF">
              <ArrowRightBold v-if="glProps.right.is" />
              <ArrowLeftBold v-else />
            </el-icon>
          </div>
        </el-main>
        <el-footer v-if="glProps.terminal.is">
          <div id="terminal">
            <div>
              <p v-for="im, ix in terminalData.logDetail" :data-c="ix == terminalData.logDetail.length - 1"
                :class="type2color(im.type)">
                {{ im.time }}：{{ im.value }}
              </p>
            </div>
          </div>
        </el-footer>
      </el-container>
      <SplitBox direction="left" id="glId" :w="glProps.gap">
        <el-aside id="glId" :width="glProps.right.w + 'px'" v-show="glProps.right.is">
          工作区
          <Work :data="WorkData" />
        </el-aside>
      </SplitBox>
    </el-container>
  </el-container>

  <div id="menu" v-if="menu.is">
    <Menu :settings="menu" />
  </div>

  <div id="nodeTem" v-if="nodeTem.attrs?.data.is">
    <autoCom v-bind="nodeTem.attrs" />
  </div>

  <div id="dblMenu" v-show="dblMenu.is">
    <div id="stencilDbl" />
  </div>


  <template v-for="con, uuid in dialogObj" :key="uuid">

    <!-- scope -->
    <template v-if="con.cell.ts === 'Scope'">
      <el-dialog v-model="con.is" draggable :modal="false" :title="con.cell.label" :close-on-click-modal="false"
        :hidden="con.hidden" :style="{ left: `calc(50vw - ${500}px / 2)`, top: '8.5vh' }" class="dialogScope">
        <el-menu mode="horizontal" popper-class="scopePopper" active-text-color="#000">
          <template v-for="(im, ix) in Scope.menu" :key="ix">
            <el-sub-menu :index="`${ix}`" class="sub">
              <template #title>{{ im.title }}</template>
              <template v-for="(im1, ix1) in im.item" :key="ix1">
                <el-sub-menu :index="`${ix}-${ix1}`" style="padding-left: 30px" v-if="im1.item">
                  <template #title>{{ im1.title }}</template>
                  <template v-for="(im2, ix2) in im1.item" :key="ix2">
                    <el-menu-item :index="`${ix}-${ix1}-${ix2}`" :style="im2.value ? null : { paddingLeft: '40px' }"
                      @click="e => Scope.clickMenu(e, con)">
                      <el-icon v-if="im2.value" size="15"><Select /></el-icon>
                      {{ im2.title }}&nbsp;&nbsp;&nbsp;
                      <span v-if="im2.tip">{{ im2.tip }}</span>
                    </el-menu-item>
                  </template>
                </el-sub-menu>
                <el-menu-item :index="`${ix}-${ix1}`" v-else @click="e => Scope.clickMenu(e, con)"
                  :style="im1.value ? null : { paddingLeft: '40px' }">
                  <el-icon v-if="im1.value" size="15"><Select /></el-icon>
                  {{ im1.title }}&nbsp;&nbsp;&nbsp;
                  <span v-if="im1.tip">{{ im1.tip }}</span>
                </el-menu-item>
                <el-divider v-if="im1.d" />
              </template>
            </el-sub-menu>
          </template>
        </el-menu>
        <div :id="uuid" class="Scope"></div>
        <div class="scopeStatus">
          <div>{{ Scope.status[0] }}</div>
          <div>
            <div>{{ Scope.status[1] }}</div>
            <div>T={{ Scope.status[2].toFixed(3) }}</div>
          </div>
        </div>
      </el-dialog>
    </template>
    <template v-else>
      <el-dialog v-model="con.is" draggable :modal="false" :title="con.illustrate?.title" :hidden="con.hidden"
        :width="con.w || 500" :close-on-click-modal="false"
        :style="{ left: `calc(50vw - ${con.w || 500}px / 2)`, top: '8.5vh' }" class="dialogTemplate">
        <labelArea v-if="con.illustrate?.content" :label="con.illustrate?.name" class="content">
          {{ con.illustrate?.content }}
        </labelArea>

        <Tabs v-model="activeName" :propData="con" />

        <template #footer>
          <div class="dialog-footer">
            <el-button @click="confirm(con)">确认</el-button>
            <el-button @click="con.is = false">取消</el-button>
            <el-button @click="help">帮助</el-button>
            <el-button @click="yingyong(con)">应用</el-button>
          </div>
        </template>
      </el-dialog>
    </template>
  </template>
</template>

<style scoped>
.el-header,
.el-main {
  padding: 0;
}

.el-footer {
  height: 180px;
  padding: 0;

  #terminal {
    user-select: text;
    position: relative;
    height: 100%;
    border: 1px solid #ccc;

    &>div {
      height: 110%;
      overflow-y: scroll;

      &::after {
        content: '终端';
        position: absolute;
        top: -27px;
        left: 23px;
        height: 27px;
        padding: 0 10px;
        background-color: #ccc;
        font-size: 14px;
        line-height: 27px;
      }

      p {
        padding: 0 20px;
        font-size: 14px;

        &.error {
          color: #fc4a4a;
        }

        &.success {
          color: #56ba24;
        }

        &.log {
          color: #0055ff;
        }
      }
    }
  }

}

#topMenu {
  width: 100%;
  height: 100%;
  background: #f5f5f5;
}

#stencilDbl {
  &>>>.x6-widget-stencil-group.collapsable {
    display: none;
  }

  &>>>.x6-widget-stencil-group-title {
    display: none;
  }

  &>>>.x6-widget-stencil .x6-node.unmatched {
    display: none;
  }

  &::after {
    content: '';
    position: fixed;
    width: 38px;
    height: 38px;
    cursor: none;
  }
}

#stencilDbl,
#stencilContainer {
  position: relative;
  width: 100%;
  height: 100%;

  &>>>.x6-widget-stencil-title {
    display: none;
  }

  &>>>.x6-widget-stencil-content {
    top: 48px;
  }

  /* 以下是强制修改stencil一排多项及单项的样式 */
  &>>>.x6-widget-stencil-group-title {
    background-color: transparent;
  }

  &>>>.x6-widget-stencil-group-content {
    height: var(--trh);
  }

  &>>>.x6-widget-stencil-group-content>.x6-graph {
    height: 100% !important;
  }

  &>>>.x6-graph-svg-stage {
    --try: 35%;
    transform: scale(0.65) translate(20%, var(--try));
  }

  &>>>.x6-widget-stencil-group.collapsable[trh] .x6-graph-svg-stage {
    --try: 0;
  }

  &>>>.x6-widget-stencil-group.collapsable[null] {
    display: none;
  }

  /*  */

  & ::-webkit-scrollbar {
    width: 0;
  }
}

.containerGap {
  --h: 24px;
  overflow: hidden;
  padding-top: v-bind("glProps.gap + 'px'");

  .el-breadcrumb:has(>span) {
    border: 1px solid #e5e5e5;
    margin: 0 23px 4px 0;

    &>span {
      height: var(--h);

      &>>>span {
        display: flex;
        align-items: center;
        height: 100%;
        padding: 0 8px;
        cursor: pointer;
      }

      &>>>i {
        margin: 0;
      }

      &>>>span:hover {
        background: linear-gradient(#fff, #a0a0a477);
      }
    }
  }
}

.el-main {
  position: relative;
  overflow: hidden;
  --c: #ccc;

  .glIdBtn {
    background-color: var(--c);
    position: absolute;
    width: 19px;
    height: 26px;
    top: 0;
    right: 0;
    justify-content: end;
    cursor: pointer;

    &::after {
      content: '';
      position: fixed;
      background-color: var(--c);
      width: 4px;
      height: 26px;
      margin-right: -4px;
    }
  }

  .terminalBtn {
    background-color: var(--c);
    position: absolute;
    width: 23px;
    height: 26px;
    bottom: 0;
    left: 0;
    cursor: pointer;
  }
}

#glId {
  background-color: #f7f7f7;
  display: flex;
  flex-direction: column;
  overflow: unset;

  &>div {
    /* background-color: #bfe5ff; */
    height: 100%;
    border: solid #ccc;
    border-width: 2px 2px 0;
  }
}

#nodeTem {
  position: fixed;
  left: v-bind("nodeTem.attrs?.data.x + 'px'");
  top: v-bind("nodeTem.attrs?.data.y + 'px'");
  user-select: all;
}

#menu {
  position: fixed;
  left: v-bind("menu.x + 'px'");
  top: v-bind("menu.y + 'px'");

  /* border-radius: 8px; */
  /* overflow: hidden; */
  box-shadow:
    0 1px 3px rgba(0, 0, 0, 0.12),
    0 1px 2px rgba(0, 0, 0, 0.24);
}

#dblMenu {
  width: 300px;
  height: 400px;
  position: fixed;
  left: v-bind("dblMenu.x + 'px'");
  top: v-bind("dblMenu.y + 'px'");
  /* border-radius: 8px; */
  /* overflow: hidden; */
  box-shadow:
    0 1px 3px rgba(0, 0, 0, 0.12),
    0 1px 2px rgba(0, 0, 0, 0.24);
}

/* >>>rect.x6-edge-tool-segment[hidden=true],
>>>path.x6-edge-tool[hidden=true] {
  display: none;
} */

>>>.x6-widget-snapline-vertical,
>>>.x6-widget-snapline-horizontal {
  stroke: #bfe5ff;
  stroke-width: 3px;
}

>>>.x6-widget-minimap {
  position: fixed;
  right: 0;
}

>>>.x6-widget-transform {
  border: 1px solid #ccc;
  border-radius: 0;
  margin: 0;
  padding: 0;
}

>>>.x6-widget-transform-resize {
  border-color: #ccc;
  border-radius: 0;
  background-color: #ffffffaa
}

>>>.x6-widget-transform-rotate {
  width: 12px;
  height: 12px;
  top: -20px;
  left: calc(50% - 8px);
  cursor: pointer;
  background: url(/rotate.png) center / cover no-repeat;
  border: 0;
}

/* >>>.x6-widget-selection-inner {
  padding: 18px;
  margin: -20px;
  border-style: dashed;
  box-shadow:
    rgba(60, 64, 67, 0.3) 0 1px 2px 0,
    rgba(60, 64, 67, 0.15) 0 2px 6px 2px;
}

>>>.x6-widget-selection-box {
  border: 0;
  box-shadow: none;
} */

>>>.x6-graph-svg-stage image {
  mix-blend-mode: multiply;
}

.x6-graph.x6-graph-pannable {
  cursor: initial;
}

.content {
  padding: 12px 10px;
  margin: 0 0 12px;
  white-space: pre-line;
  line-height: 1;
}

>>>.ellipsis>div {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: center;
}
</style>

<style>
::-webkit-scrollbar {
  width: 0;
}

.dialogScope {
  padding: 0;
  width: fit-content;

  & header {
    padding: 16px 16px 0;

    .el-dialog__title {
      padding-left: 22px;
    }
  }

  .el-dialog__body {
    --scopeSub: 30px;
    min-width: 500px;
    min-height: 300px;
    width: 600px;
    height: 400px;
    resize: both;
    overflow: hidden;

    .el-menu--horizontal.el-menu {
      height: var(--scopeSub);
    }

    .sub {

      &:hover,
      &.is-opened {
        .el-sub-menu__title {
          color: #000;
          background-color: #c0dcf3;
          border: 1px solid #90c8f6;
        }
      }

      .el-sub-menu__title {
        font-size: 12px;
        padding: 0 10px;
        border: 1px solid transparent;

        & i {
          display: none;
        }
      }
    }
  }

  ::-webkit-scrollbar {
    width: 0;
  }
}

.scopePopper {
  & li span {
    margin-left: auto;
  }

  .el-divider--horizontal {
    margin: 0;
  }
}

.el-dialog__title {
  font-size: 14px;
  background-color: aqua;
  background: url(/sign.png) no-repeat;
}

.dialogTemplate .el-dialog__title {
  padding-left: 85px;
  position: relative;

  &::after {
    content: '模块参数:';
    position: absolute;
    width: 100px;
    line-height: 1.4;
    inset: 0 0 0 22px;
  }
}

.Scope {
  width: 100%;
  height: calc(100% - var(--scopeSub) - 20px);
  background-color: #000;

  .scopeTooltip {
    background-color: #000 !important;
    color: #fefefe !important;

    &>div {
      &:first-child {
        margin-left: 22.5px;
      }
    }
  }
}

.scopeStatus {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 10px;
  height: 20px;
  padding: 0 3px;
  background-color: #f0f0f0;

  &>div {
    display: flex;

    &>div {
      padding: 0 6px;
      margin: 0 2px;
      border: 1px solid;
      border-color: #b3b3b3 #fff #fff #b3b3b3;
    }
  }
}

.el-select__placeholder.is-transparent {
  display: none;
}
</style>
